Accessing Firebase Admin across Express Routes - javascript

I'm using Cloud Functions and Firestore.
In my index.js, I initialize Firebase like so:
index.js
const admin = require("firebase-admin");
const functions = require("firebase-functions");
const usersApi = require("./api/users")
const paymentsApi = require("./api/payments")
const express = require('express');
const cors = require('cors');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore()
const app = express();
const checkHeader = async(req, res, next) => {
if(req.headers.authorization) {
admin.auth().verifyIdToken(req.headers.authorization)
.then(token => {
req.uid = token.uid;
req.email = token.email;
req.stripeID = token.stripeID || null;
return next();
})
.catch(e => {
return next(e.errorInfo.code)
})
} else {
return next('No token found');
}
}
app.use(cors({origin: true}));
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(checkHeader);
app.disable("x-powered-by");
app.use("/users", usersApi)
app.use("/payments", paymentsApi)
exports.api = functions.https.onRequest(app)
Then in my users router, i have the following:
api/users/index.js
const express = require('express');
const admin = require("firebase-admin");
const userRouter = express.Router();
const functions = require("firebase-functions");
const db = admin.firestore();
userRouter.post('/addUser', (req, res) => {
return adminT.collection('users').doc(req.uid).set({
activeSub: false,
name: req.body.name
})
.catch(err => {
throw new functions.https.HttpsError('unknown', err.message, {success:false, error: {err}})
})
})
userRouter.post("*", (req, res) => {
res.status(404).send("This route does not exist");
})
module.exports = userRouter;
I seem to get a couple of errors depending on how I configure my code.
The first one in the setup as above is:
Error: The default Firebase app does not exist. Make sure you call
initializeApp() before using any of the Firebase services.
If I then initialize it within my Users route, I get told that the default Firebase instance has already been initialized.
How do I go about initalizing Firebase once, and then using it throughout my Cloud Function App?

So I managed to get this working in the end.
In my index.js, I removed:
admin.initializeApp(functions.config().firebase);
const db = admin.firestore()
I created a file called fb.js:
const admin = require("firebase-admin");
const functions = require("firebase-functions");
module.exports = admin.initializeApp(functions.config().firebase);
Then edited my routes like so:
const express = require('express');
const userRouter = express.Router();
const fb = require('../../fb');
const db = fb.firestore()
userRouter.post('/addUser', (req, res) => {
return db.collection('users').doc(req.uid).set({
activeSub: false,
name: req.body.name
})
.catch(err => {
throw new functions.https.HttpsError('unknown', err.message, {success:false, error: {err}})
})
})
userRouter.post("*", (req, res) => {
res.status(404).send("This route does not exist");
})
module.exports = userRouter;

Related

Firebase functions can't access my middleware routes

///index.js
const functions = require("firebase-functions");
const express = require("express");
const app = express();
const productRouter = require('./routes/productRoutes');
const globalErrorHandler = require('./controllers/errorController');
const AppError = require('./utils/appError');
// Compressing upcompressed files which is been sent to client such text.
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'));
}
// app.use(express.static(path.join(__dirname, 'public')));
app.get('/', (req, res) => {
res.send('Hello World')
});
app.get('/homepage', (req, res) => {
res.send('Hello People of God')
});
app.use('/products', productRouter);
// Handing Unhandled Routes
app.all('*', (req, res, next) => {
next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404));
});
app.use(globalErrorHandler);
exports.app = functions.https.onRequest(app);
///productRoutes.js
const express = require('express');
const {
getProduct,
getAllProduct,
} = require('./../controllers/productController');
const router = express.Router();
router
.route('/')
.get(getAllProduct);
router
.route('/:id')
.get(getProduct);
module.exports = router;
///productController.js
const AppError = require('../utils/appError');
const Product = require('../modals/productModels');
const catchAsync = require('../utils/catchAsync');
// GET SINGLE PRODUCT CONTROLLER
exports.getProduct = catchAsync(async (req, res, next) => {
const product = await Product.findById(req.params.id)
.populate('reviews');
if (!product) {
return next(new AppError('No product found with that ID', 404));
}
res.status(200).json({
status: 'success',
data: {
product
}
});
});
// GET ALL PRODUCT CONTROLLER
exports.getAllProduct = catchAsync(async (req, res, next) => {
const products = await Product.find();
res.status(200).json({
status: 'success',
results: products.length,
data: {
products
}
});
});
///server.js
const mongoose = require('mongoose');
const app = require('./index')
const dotenv = require('dotenv');
// CONNECTING TO MONGODB SERVER
dotenv.config({ path: './config.env' })
const DB = process.env.DATABASE.replace('<PASSWORD>', process.env.DATABASE_PASSWORD);
mongoose.connect(DB, {
useNewUrlParser: true,
safe: true,
strict: false,
useUnifiedTopology: true
}).then(con => console.log('DB connection successful'))
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`App running on port ${port}...`);
});
After running function serve on my terminal i can access the two '/' and 'homepage' app.get which return the res "Hello world" and "Hello people of God" but can't access app.use('/products', productRouter). Its does take some time to run and throw an error "{"code":"ECONNRESET"}" please why is this so.
Am expecting to get my list of products from my mongodb data base.

TypeError: app.use() requires a middleware function No clue why error

so I am getting the the error : TypeError: app.use() requires a middleware function.
I am unsure why i made sure i exported the file, but I am a bit confused why. I think i linked it up correctly in my server file as well.
const express = require('express')
const mongoose = require('mongoose')
const morgan = require('morgan')
const app = express()
const bodyParser = require('body-parser')
app.use(morgan('dev'))
require('dotenv').config()
const { expressjwt: jwt } = require("express-jwt")
app.use(bodyParser.json())
mongoose.connect('mongodb://localhost:27017/recipe', ()=> console.log('connected to database'))
app.use('/auth', require('./routes/authRouter'))
app.use("/api", jwt( {secret: process.env.SECRET, algorithms: ['HS256']}))
app.use('/api/recipe/', require('./routes/recipeRoutes'))
app.use('/api/public/'), require('./routes/publicDelete')
app.use('/api/comments/', require('./routes/commentRouter'))
app.use('/api/ingredients/', require('./routes/ingredRouter'))
app.use((err,req,res,next)=>{
console.log(err)
if(err.name ==='UnauthorizedError'){
res.status(err.status)
}
return res.send({message:err.message})
})
app.listen(8000, ()=>{
console.log('hello ')
})
const express = require('express')
const publicDeleteRouter = express.Router()
const Recipe = require('../models/recipe')
const User = require('../models/user')
publicDeleteRouter.delete('/:recipeId', (req, res, next) =>{
Recipe.findOneAndDelete({idMeal: req.params.recipeId, user: req.auth._id}, (err, deletedRecipe) => {
if(err){
console.log(idMeal)
res.status(404)
return next(err)
}
console.log('successfully deleted: ', deletedRecipe)
return res.status(200).send(`Successfully deleted ${deletedRecipe}`)
})
})
module.exports = publicDeleteRouter

MongoDb query return empty object

I'm trying to perform a simple .find() query on my mongodbAtlas, but the result of this query is an empty object.
this is my server file:
require("dotenv").config({ path: "./config.env" });
const { MongoClient, ServerApiVersion } = require("mongodb");
const express = require("express");
const { ServiceBroker } = require("moleculer");
const AUTH_SERVICE = require("./controller/services/auth/auth.service");
global.broker = new ServiceBroker({
nodeID: "auth",
});
const app = express();
app.use(express.json());
app.use("/auth", require("./routes/auth"));
const { PORT, URI } = process.env || 5000;
global.broker.createService(AUTH_SERVICE);
const start = async () => {
const dba = await MongoClient.connect(URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
global.db = dba.db("Auth");
try {
await dba.connect();
console.log("DATABASE CONNESSO CON SUCCESSOπŸ“‘");
} catch (e) {
console.error(e);
}
await global.broker.start();
app.listen(PORT, () => console.log(`PORT IT'S UP AND RUNNING πŸš€ ON ${PORT}`));
};
start();
this is my routes file:
const express = require("express");
const router = express.Router();
router.get("/register", async (req, res) => {
const data = global.db.collection("Users").find({}).toArray();
res.send(data);
});
module.exports = router;
this is how my document is populated:
{"_id":{"$oid":"6297bbc83a95b81d74882f65"},"username":"Test","email":"test#gmail.com","password":"1234"}
I think you are missing the "await" keyword after const data..... as API data fetching calls are asynchronous and required promise/ async-await to handle. Being async in nature, it moves forward to the next instruction and returns an empty array.
const express = require("express");
const router = express.Router();
router.get("/register", async (req, res) => {
const data = await global.db.collection("Users").find({}).toArray();
res.send(data);
});
module.exports = router;

Cannot read property 'collection' of undefined MongoDB nodejs driver

i'm trying to pass the mongodb connection to all the other routes, so i created another file and imported mongoClient there and wrapped connect and getDb in functions so i can connect to the db first from server.js and then access the db from the other files, but idk why i'm getting Cannot read property 'collection' of undefined
server.js
const express = require('express')
const bodyParser = require('body-parser')
const mongodb = require('./mongodb/db.js')
const auth = require('./routes/auth.js')
require('dotenv').config()
const app = express();
app.use(bodyParser.json());
const PORT = process.env.PORT
app.get('/api', (req, res) => {
res.send('Welcome to the api')
})
app.use('/api/auth', auth);
mongodb.connect(() => {
app.listen(PORT, () => {
console.log(`app is listening at http://localhost:${PORT}`)
})
})
./mongodb/db.js
const MongoClient = require('mongodb').MongoClient;
const DB_URL = process.env.DB_URL
const DB_NAME = process.env.DB_NAME
const dbClient = new MongoClient(DB_URL, { useUnifiedTopology: true })
let db;
const connect = (callback) => {
dbClient.connect().then(client => {
db = client.db(DB_NAME)
console.log("connected to db")
}).catch(console.log)
callback()
}
const get = () => {
return db;
}
module.exports = {
connect,
get
};
./routes/auth.js
const express = require('express')
const router = express.Router();
const db = require('../mongodb/db.js');
const smth = db.get();
console.log(smth) //undefined;
const usersCollection = db.get().collection('users');
const authCollection = db.get().collection('auth')
router.post('/login', async (req, res) => {
...
})
router.post('/register', async (req, res) => {
...
})
module.exports = router
You call callback outside of the promise chain in the connect function of ./mongodb/db.js. It's possible that you are running into some async issues there, as the function can return before the promise chain resolves.

Using exports module in node

I am creating a small project in node, i have routes of two tables, drums and pianos in same file named routes.js in two different function called drums() for drums and pianos() for pianos. i have exported both functions and both of them return router. Router is return value of express.Route() class.
I have used these two functions in index.js file by importing, i am accessing route by following code
app.use('/pianos', routes.pianos());
app.use('/drums', routes.drums());
But i am getting response from only one function, if i keep
app.use('/pianos', routes.pianos());
at first then i get list of pianos with url localhost/pianos and localhost/drums as well and if i keep
app.use('/drums', routes.drums());
at first then i get list of drums with url localhost/drums and localhost/pianos as well. What is mistake here?
const express = require('express');
const router = express.Router();
const joi = require('joi');
function drums(){
drums = [
{id:1, name:'Bongo drum'},
{id:2, name:'Bass drum'},
{id:3, name:'Ashiko'},
{id:4, name:'Basler drum'},
{id:5, name:'CajΓ³n'}
];
router.get('/', (req, res)=>{
res.send(drums);
});
router.get('/:id', (req, res) =>{
const drum = drums.find( c=> c.id === parseInt(req.params.id));
if(!drum){
return res.status(404).send("Error: Drum is not available");
}
res.send(drum.name);
});
router.post('/', (req, res)=>{
const {error} = validator(req.body);
if(error) return res.status(400).send('Eror 400: Bad request', error.details[0].message);
const drum = {id:drums.length+1, name:req.body.name};
drums.push(drum);
res.send(drum);
}
);
router.put('/:id', (req, res)=>{
const drum = drums.find(c=> c.id === parseInt(req.params.id));
if(!drum) return res.status(404).send('Error: Drum is not available');
const {error} = validator(req.body);
if(error) return res.status(400).send('Error 400: Bad request', error.details[0].message);
drum.name = req.body.name;
res.send(drum);
});
return router;
}
function pianos(){
const pianos = [
{id:1, name:'Yamaha U1'},
{id:2, name:'Roland RD-2000'},
{id:3, name:'Nord Piano 4'},
{id:4, name:'Yamaha NU1X'},
{id:5, name:' Dexibell Vivo S7'}
];
router.get('/', (req, res)=>{
res.send(pianos);
});
router.get('/:id', (req, res)=>{
const piano = pioanos.find(c=> c.id === parseInt(req.params.id));
if(!piano) return res.status(404).send('Error:The piano is not available');
res.send(piano);
});
router.post('/', (req, res)=>{
const {error} = validator(req.body);
if(error) return res.status(400).send('Error-Bad request', error.details[0].message);
const piano = {id:pianos.length+1, name:req.body.name};
pianos.push(piano);
res.send(piano);
});
router.put('/:id', (res, req)=>{
const piano = pioanos.find(c=> c.id === parseInt(req.params.id));
if(!piano) return res.status(404).send('Error: Piano is the available');
const {error} = validator(req.body);
if(error) return res.status(400).send('Error:Bad request', error.details[0].message);
piano.name = req.body.name;
res.send(piano);
});
return router;
}
function validator(instrument){
const schema={
name:joi.string().min(5).required()
};
return joi.validate(instrument, schema);
}
module.exports.drums = drums;
module.exports.pianos = pianos;
MY index.js file is like this:
const mongoose = require('mongoose');
const express = require('express');
const app = express();
const debug = require('debug')('pm:index');
const routes = require('./routes/routes');
mongoose.connect('mongodb:localhost/planetmusic')
.then(()=> debug('Connected to database'))
.catch(err => debug('Error!!Could not connect to database', err.message));
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(express.static('public'));
app.use('/drums', routes.drums());
app.use('/pianos', routes.pianos());
const port = process.env.port || 5000;
app.listen(port, ()=> console.log(`listening at port: ${port}`));
If there are other better solution to manage my all routes then please help me.
taken that we should separate concerns, I would recommend split everything into files, it is easier to maintain and to add features in the future as everything is in it's own place...
in a NodeJs project, it is normal to encounter, in examples, some folder rearrangement, and I would suggest this:
.
β”œβ”€β”€ package.json
└── server/
β”œβ”€β”€ server.js
└── routes/
β”œβ”€β”€ drums.js
β”œβ”€β”€ index.js
└── piano.js
here is a very simple example, so you can understand how ExpressJs routes work when spread out upon multiple files:
server.js content
const express = require('express');
const routes = require('./routes');
const app = express();
const PORT = 5001;
app.use('/drums', routes.drums);
app.use('/pianos', routes.pianos);
app.use('/', (req, res) => { res.send({ action: 'default get' }); });
app.listen(PORT, () => {
require('./utilities/api-table')(app._router.stack);
console.log(`ready on http://localhost:${PORT}`);
});
server/routes/index.js content
const fs = require("fs");
const path = require("path");
const routing = {};
fs.readdirSync(__dirname) // read all files in this directory
.filter(
file =>
// only read .js files, but not the index.js (this file)
file.indexOf(".") !== 0 && file !== "index.js" && file.slice(-3) === ".js"
)
.forEach(file => {
const filename = file.replace(".js", "");
// attach the routes in it's own filename
routing[filename] = require(path.join(__dirname, file));
});
module.exports = routing;
drums.js and pianos.js content are exactly the same, just the "drums" and "pianos" change so we know we are reading from the correct file...
const express = require('express');
const router = express.Router();
const getAll = (req, res) => {
res.send({ action: 'GET all drums' })
};
const create = (req, res) => {
res.send({ action: 'POST drums', data: res.body })
};
const getById = (req, res) => {
res.send({ action: 'GET drums by id', id: req.params.id })
};
const editById = (req, res) => {
res.send({ action: 'PUT drums by id', id: req.params.id })
};
const deleteById = (req, res) => {
res.send({ action: 'DEL drums by id', id: req.params.id })
};
router.get('/', getAll);
router.post('/', create);
router.get('/:id', getById);
router.put('/:id', editById);
router.delete('/:id', deleteById);
module.exports = router;
when you fire up the server up, you will get all this routes:
and output as
full project (though it's very simple) can be found on GitHub

Categories

Resources