I have multiple controllers and each controller has multiple methods. In each method I authenticate the user and use the user id returned from the authentication to get the data from database. I am trying to create reusable code for authentication since the code is repeated.
In the controller:
const authenticate = require('../utils/user-authenticate');
exports.getData = async (req, res, next) => {
const userId = await authenticate.user(req, res, next);
console.log(userId);
};
And in the authentication I have:
exports.user = (req, res, next) => passport.authenticate('jwt', async (error, result) => {
if (error) {
// Send response using res.status(401);
} else {
return result;
}
})(req, res, next);
The console.log(userId); prints undefined always. This is print before passport finishes. Looks like async/await does not work the way I want here.
It works if I use await authenticate.user(req, res, next).then() but isn't it possible to assign the result directly to userId variable?
If I use return next('1'): first time undefined but second time it prints 1.
wrapped into a promise:
exports.user = (req, res, next) => new Promise((resolve, reject) => {
passport.authenticate('jwt', async (error, result) => {
if (error) {
// reject(error)
// Send response using res.status(401);
} else {
resolve(result);
}
})(req, res, next);
})
but think about:
//app.use or something similar
addMiddleware(authJWT);
// later in the chain
useMiddleware((req, res, next)=>{
// test auth or end chain
if(!req.JWT_user) return;
req.customField = 'one for the chain'
// process next middleware
next()
});
Thanks #Estradiaz for the suggestion:
exports.user returns undefined ... Return is scoped within inner
callback - if you want to pass it outside wrap it into a promise
Reusable passport.authenticate:
exports.user = (req, res) => {
return new Promise(resolve => {
passport.authenticate('jwt', null, async (error, result) => {
if (error) {
email.sendError(res, error, null);
} else if (result) {
resolve(result);
} else {
return res.status(401).json({errors: responses['1']});
}
})(req, res);
});
};
And this is how I use it in my controller, for instance in a function:
exports.getData = async (req, res, next) => {
const userId = await authenticate.user(req, res);
};
Related
So I have something like this in one of my controllers:
module.exports.authToken = (req, res, next) => {
const token = req.cookies.jwt;
//console.log(token);
if (!token) {
return res.sendStatus(403);
}
try {
const data = jwt.verify(token, "secret token");
console.log(data);
req.userId = data.id;
return next();
} catch {
return res.sendStatus(403);
}
};
and it's called by a route:
router.get("/protected", authController.authToken, (req, res) => {
return res.json({ user: { id: req.userId, role: req.userRole } });
});
and I want to get a JSON response of that route in one of my other controllers. I tried some things but none of it worked.
What I would do is abstract the response out to a function for re-use:
// the function will just return the data without writing it to the response
function protectedRoute(req) {
return {user: {id: req.userId, role: req.userRole}};
}
router.get("/protected", authController.authToken, (req, res) => {
// in the actual handler you can return the response
return res.json(protectedRoute(req));
});
// make sure the middleware is still being run
router.get("/other_route", authController.authToken, (req, res) => {
// use the same function to get the response from /protected
const protectedResponse = protectedRoute(req);
// do stuff with it
});
I am using express.js and I am trying to create a catchall async error handler.
Say I have three routes which all may throw error,
const app = express()
app.get('/user', async function(req,res) {
const res = await getUsers()
})
app.get('/questions', async function(req,res) {
const res = await getQuestions()
})
app.get('/answers', async function(req,res) {
const res = await getAnswers()
})
in these three routes, all getXX function might throw error.
I want to have just one async handler for all routes. Something like this
app.use(asyncHandler)
so I don't have to try/catch every place that may throw error. Is there any solution for this?
Thanks in advance!
It's best practice to write async/await in try/catch block. because await only takes resolve output from promise if error throw from await function it handles by the catch block.
In catch block next(error) called which go throw all following middleware until it is not found error handling minddler.
const app = require("../../Reference/node-post-demo/app")
app.get('/user', async function (req, res, next) {
try {
const res = await getUsers();
return res.json(res);
} catch (error) {
next(error)
}
})
app.get('/questions', async function (req, res, next) {
try {
const res = await getQuestions()
return res.json(res);
} catch (error) {
next(error)
}
})
app.get('/answers', async function (req, res, next) {
try {
const res = await getAnswers()
return res.json(res);
} catch (error) {
next(error)
}
})
app.use(async (error, req, res) => {
//here, your error hendling code
})
I'm new to javascript, nodejs, and express, and confused of using next().
I want my code to move on to the next router with next(), but it seems to move on to the next then.
My code:
//validation
router.post('/requests', (req, res, next) => {
let {myData} = req.body
basicCheck(res, cluster, myData)
.then(() => {
if (myCheck()) {
next()
return // Doesn't need to do rest of the code. Just move on to the next router.post
}
...
return Promise.all(somePromises)
})
.then(() => {
...
return Promise.all(somePromises)
})
.then(() => {
if (someCheck() {
next()
} else {
res.status(400).send('message') // My code reaches here! even when myCheck() is true
}
})
.catch((err) => {
...
})
})
// where next() needs to be
router.post('/requests', (req, res) => {
...
})
When next() is outside the basicCheck, next() goes to the next router.post.
I don't get the concept of where next() indicates.
How can I correct this code while doing myCheck() inside basicCheck()?
With next() you move to the next middleware.
Exapmle:
You have a route like:
app.get("/", (req, res, next) => {
res.send("hello")
})
Instead of using an anonymous function you can declare an function and use it it like:
function firstMiddleware(req, res, next){
res.send("hello")
}
app.get("/", firstMiddleware);
What you can do is you can have multiple middlewares in your route like:
function firstMiddleware(req, res, next){
console.log("hy");
next()
}
function secondMiddleware(req,res,next) {
console.log("hello")
res.send("hello");
}
app.get("/", firstMiddleware, secondMiddleware);
As you can see. In my first middleware i use next(). This tells express.js to move to the next middleware in this case secondMiddleware
The middlewares gets executed from the left to right and with next() you tell them to move to the next until you are on the end.
Usually the last middleware is your API endpoint and you should not use next() otherwise you would "jump out" of your route and you would receive an error if you have defined an global error handler
Also sidenote: A bonus would be to seperate your routes and logic by creating an file called controller.js for example.
controller.js
function firstMiddleware(req, res, next){
console.log("hy");
next()
}
function secondMiddleware(req,res,next) {
console.log("hello")
res.send("hello");
}
module.exports = {
firstMiddleware,
secondMiddleware
}
Now you can import it:
const { firstMiddleware, secondMiddleware } = require("./controller.js");
app.get("/", firstMiddleware, secondMiddleware);
This makes your code easier to maintain as it grows
EDIT:
router.post("/requests", async (req, res, next) => {
let { myData } = req.body;
let checkresult = await awbasicCheck(res, cluster, myData);
if (myCheck()) {
return next();
}
let someResults = await Promise.all(somePromises);
let someMoreResults = await Promise.all(somePromises);
if (someCheck()) {
return next();
} else {
res.status(400).send("message"); // My code reaches here! even when myCheck() is true
}
});
You use return witch yes stops the function from execution BUT what you also do is an promise chaining.
I have written here an async / await approach
I have several .get requests that I normally call, like this:
notesController
controller.get('/customers/', async (req, res, next) => {
const customers = await Customer.find();
res.status(200).send(customers);
});
controller.get('/documents/', async (req, res, next) => {
const orders = await Order.find();
res.status(200).send(orders);
});
In certain circumstances, I'd like to be able to call both at once, like this:
controller.get('/version/', async (req, res, next) => {
const ver = await Version.findById(req.headers.sub);
if (req.headers.dbversion === ver.dbversion) {
res.status(200).send({ versionMatch: true });
} else {
req.url = '/customers/';
const custData = await controller.handle(req, res, next);
req.url = '/orders/';
const orders = await controller.handle(req, res, next);
res.status(200).send({ customers: custData, docs: invoices });
}
});
But this doesn't work. Adding a console message in my .get('/customers') function, I can see that it is being called, but isn't returning any data. My desired result is that I make one API call, and if the conditions are met, it returns both sets of data at once. How do I best achieve that?
This is not possible with express. Your best bet is make the functions that handle the request standalone functions, and when your conditions are met call them to get both data sets.
controller.get('/customers/', async (req, res, next) => {
const customers = findCustomers(args);
if (condition is met) {
const orders = findOrders();
}
res.status(200).send({ customers, orders });
});
controller.get('/documents/', async (req, res, next) => {
const orders = findOrders(args);
res.status(200).send(orders);
});
function findCustomers(args) {
const customers = await Customer.find();
return customers;
}
function findOrders(args) {
const orders = await Order.find();
return orders
}
I am trying to write a wrapper for express callback function displayUsers() in this case, it would append error handling logic to avoid using try catch everywhere.
The main issue is that fn() actually executes before being invoked by router.get
I am not sure why as I am returning it within a function, not executing it.
///Userroute.js
var router = require('express').Router();
var userModel = require('../models/user');
var asyncErrorHandler = require('../helpers/asyncErrorHandler');
var viewsDir = '../views/';
// get users from model
var displayUsers = async function(req, res, next) {
var users = await userModel.getUsers();
console.log(users);
res.render(`${viewsDir}/users.hbs`, {users:users})
};
var safeDisplayUsersfn = asyncErrorHandler(displayUsers);
router.get('/', safeDisplayUsersfn);
//asyncErrorHandler.js
module.exports = function (fn) {
return async function(req, res) {
try{
await fn();
}catch(error){
console.log('Error happened' + error);
res.status(500).send('Unexpected Error');
}finally{
}
}
}
fn() was executing correctly, what needed to be done is to pass parameters to the executing function: fn(req,res,next); Like:
module.exports = function (fn) {
return async function(req, res, next) {
try{
await fn(req, res, next);
}catch(error){
console.log('Error happened' + error);
res.status(500).send('Unexpected Error');
}finally{
}
}
}