I'm using express middleware with NodeJS. I organized my routes with no callbacks, but for some of them it's better to mutualize functions, so I began to write some callbacks.
I have for example this function (I don't put the whole code, it's useless for my question here):
async function verification(req, res, next) {
And I use it like this in the route part:
router.post('/item/:id/dosomething',
verification,
async (req, res) => {
Everything works well, but if I want to continue using callbacks (sometimes it's a good solution to have a clean and efficient code), I'll have to pass some arguments to the function. Of course I tried this, and it didn't work:
router.post('/item/:id/dosomething',
verification(arg1, arg2),
async (req, res) => {
I searched on StackOverflow answers for "pass arguments to a callback function" but the few that could be interesting talked about a wrapping function, which I don't think I can implement in my case.
If anybody could give me a hand, that would be great. Thanks :)
Here is a snippet of how a next worked without writing it in the call (look at the verif):
The callback:
async function verif(req, res, next) {
let rows;
const {
id
} = req.params;
({ rows } = await db.query(`XXX`))
if (rows.length === 1) {
return next();
} else {
retour(req, res, 500, "No can do.");
}
Where it's called:
router.post('/mymusic/:id/addElements',
droits.verifRightsCB.bind(undefined, 'music', 'addInMyMusic'),
verif,
async (req, res) => {...
The last part of the route (async (req, res) =>) is only executed when the next() condition is fulfilled in the verif, although I didn't pass any argument.
function verification(arg1, arg2, req, res, next) {...}
router.post('/item/:id/dosomething',
verification.bind(undefined, arg1, arg2),
(req, res) => {...}
(The functions need not be async.)
Note that the router.post statement is executed during server start-up, not per request. Therefore you cannot write something like
router.post('/item/:id/dosomething',
verification.bind(undefined, arg1, req.params.arg),
(req, res) => {...}
because there is no req during server start-up. Instead, you may write
router.post('/item/:id/dosomething',
(req, res, next) => verification(arg1, req.params.arg, req, res, next),
(req, res) => {...}
The verification function can then
call next() after successful verification to invoke the other middleware function ((req, res) => {...})
or call next(err) after a verification error in order to skip the other middleware function and report the error.
Related
I am wondering how express.js works when you specify multiple argument in the app.get, for example:
app.get('/', requireAuth, (req, res) => { })
Here, I believe you have the slash "/" as the route, and (req, res) as arguments for the callback function. But there is also the 'requireAuth' part which was used for authorization. But in the documentation I don't find anything about multiple arguments in the way we use 'requireAuth' now. It does mention that you can use an array of functions but that is not used here (https://expressjs.com/en/guide/routing.html). How does it work?
P.S. The 'requireAuth' code for reference:
module.exports = (req, res, next) => {
const { authorization } = req.headers;
// authorization === 'Bearer laksjdflaksdjasdfklj'
if (!authorization) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const token = authorization.replace('Bearer ', '');
jwt.verify(token, 'MY_SECRET_KEY', async (err, payload) => {
if (err) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const { userId } = payload;
const user = await User.findById(userId);
req.user = user;
next();
});
};
All the arguments are simply middle-ware functions that you call before the actual route logic is run(for individual routes). As long as there is next handling in each of the middle-ware functions, the chain will run till your route logic, and then exit where next isn't handled.
This is the magic of express. You can do many things with it, like error handling, handling different content types etc.
I have a express app.js with typical
app.get('/path1', (req, res => {})
app.get('/path2', (req, res => {})
app.get('/path3', (req, res => {})
now I want to catch all routes, starting with api such as below and redirect them to their corresponding handler in express but not sure how to achieve that
/api/path1
/api/path2
/api/path3
I' assuming i can have a catch all api as below
app.all('/api/*', function (request, response, next) { //in a server.js file
//how can i call the corresponding paths here??
// looking for something to do forward to current-route.replace('api','')
// or something like that
})
Maybe a router-level middleware could solve your problem:
const router = express.Router();
router.get('/path1', (req, res => {});
router.get('/path2', (req, res => {});
router.get('/path3', (req, res => {});
app.use('/api', router);
Update:
Use redirect (not that much of a difference to your current solution; not tested):
app.all('/api/*', (request, response) => res.redirect(request.url.replace('/api', '')));
this worked for me, please let me know if there is a better way
app.all('/api/*', function (request, response, next) {
request.url = request.url.replace('/api','');
next();
})
I'm trying to use passport.authenticate('local') as middleware with passport-local-mongoose but it doesn't seem to pass control to the subsequent middlewares in my route.
I'm assuming there's an error but the following (err, req, res, next) middleware isn't being called so I'm wondering if I've misunderstood something basic about passport-local in general.
I've spent a few hours trying various things, searching here but I can't find a way to isolate my problem any further or get a better log of where control is going wrong in my route.
I've posted a small reproducible example to GitHub.
This is how I'm setting up BEFORE my routes:
// Get connected
mongoose.connect('mongodb://localhost/pass');
mongoose.Promise = global.Promise;
mongoose.connection.on('error', (err) => { console.error(err.message) });
// Basic user schema using nickname field as username
const Schema = mongoose.Schema;
const userSchema = new Schema({});
userSchema.plugin(passportLocalMongoose, { usernameField: 'nickname' });
const User = mongoose.model('User', userSchema);
// Initialise passport before routes
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser);
passport.deserializeUser(User.deserializeUser);
app.use(passport.initialize());
And this is the route with my passport.authenticate:
app.post('/login',
(req, res, next) => {
console.log('login posted ok');
next();
},
passport.authenticate('local'),
(req, res) => res.send('login successful'),
(err, req, res, next) => {
console.log(err);
res.send('login unsuccessful');
}
);
There are other routes with the pug views and registration.
Registration works fine, in mongo db.users.find() show a good looking entry for the new user.
But my /login post route doesn't get beyond passport.authenticate.
The console.log gets triggered, so I know the route is being called.
Based on my limited knowledge of express and passport, I'm expecting one of those two following middlewares to be triggered, one on success and one if it fails.
Neither is triggering.
Best way to isolate is covered in the authenticate docs under "Custom Callback", I just didn't understand it originally.
NOTE: I've saved the following in the answer branch on my repo, as posted in the question.
app.post('/login',
(req, res, next) => {
console.log('login posted ok');
next();
},
(req, res, next) => {
console.log('authenticating...')
passport.authenticate('local', (err, user, info) => {
console.log(req, res, err, user, info);
next(err);
}) (req, res, next);
},
(req, res) => res.send('login successful'),
(err, req, res, next) => {
console.log(err);
res.send('login unsuccessful');
}
);
What I realised
passport doesn't consider authentication FAIL as an err
it just leaves user null
basically you need to give it success and failure redirects if you want to use middleware form, don't do what I did and try to handle err etc.
How does the the error handler get triggered?
In sample code, I find that it is placed at the bottom of all the middleware functions. Is the position important?
You can refer below example for some details.
Here, for the '/' GET endpoint a middleware explicitly throws an error 'problem error'.
At this point, express error handler mechanism is triggered and it looks for an error handler (with err as a param). As a result, subsequent 'Hello' is not sent back to the client as its handler is not an error one.
Subsequent error handlers logErrors, clientErrorHandler and errorHandler are invoked one by one to perform relevant tasks with final one writing back the response.
The reason they are placed at the end is to catch the errors thrown by declared middlewares and handle them elegantly i.e. printing it, logging it, sending mails etc. Think of it in terms of a try catch mechanism in other languages like Java. If declared above other middlewares, they would render useless as the errors won't be handled. You can see the difference in output by swapping the order of 'GET' request with the error handlers.
const express = require('express');
const app = express();
app.get('/', (req, res, next) => next(new Error('problem error')), (req, res) => {
res.status(200).send("Hello");
});
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
function logErrors (err, req, res, next) {
console.error(err.stack)
next(err)
}
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}
function errorHandler (err, req, res, next) {
if (res.headersSent) {
return next(err)
}
res.status(500)
res.render('error', { error: err })
}
app.listen(3000, () => console.log('Example app listening on port 3000!'))
I actually have a couple of questions about Express middleware chaining and thought I'd group them in this single post since they're closely related.
Q1: In a middleware function, if I send() the Response, pass to the next function, or just return, does any of the remaining code get executed?
router.get('/a', (req, res, next) => {
res.sendStatus(200);
// Does this ever get executed?
});
router.get('/b', (req, res, next) => {
next();
// Does this ever get executed?
});
router.get('/c', (req, res, next) => {
return;
// Does this stop the middleware chain? What happens to the request/response?
});
Q2: Can you pass on to another middleware function from inside of the preceding one? My reason for this is that I have to supply an argument to create a certain middleware, and that argument is dependent on which Express app object is the owner of the Request. So I need to access a property of the Request before it is passed to the middleware that needs to be created using that property. Would this work: ?
router.get('/d', (req, res, next) => {
var middlewareFunction = createDynamicMiddleware();
middlewareFunction(req, res, next); // Will this still work as expected?
});
Q3: How are the Request/Response arguments passed to the next middleware when you call next() without any arguments?
Q1: In a middleware function, if I send() the Response, pass to the next function, or just return, does any of the remaining code get
executed?
router.get('/a', (req, res, next) => {
res.sendStatus(200);
// Does this ever get executed?
// Yes, but don't call next() or res.send() or it will throw an error
});
router.get('/b', (req, res, next) => {
next();
// Does this ever get executed?
// Yes, but don't call next() or res.send() or it will throw an error
});
router.get('/c', (req, res, next) => {
return;
// Does this stop the middleware chain? What happens to the request/response?
// The request get stuck and after a while it will fail due 'timeout'
});
Q2: Can you pass on to another middleware function from inside of the preceding one? My reason for this is that I have to supply an
argument to create a certain middleware, and that argument is
dependent on which Express app object is the owner of the Request. So
I need to access a property of the Request before it is passed to the
middleware that needs to be created using that property.
Your factory of dynamic middleware should return a new function
Functions that returns new functions, are also knows as curried functions, here is a practical example:
// based on 'someId' we will return a specific middleware
req.get('/user/:someId',
createDynamicMiddleware,
(req,res) => {
res.send('ok')
})
function createDynamicMiddleware(req,res,next){
if(req.params.someId === 'me') {
return function(req, res, next){
// do some stuff if someId is me
return next();
}
} else {
return function(req, res, next){
// do some other stuff otherwise
return next();
}
}
}
Q3: How are the Request/Response arguments passed to the next middleware when you call next() without any arguments?
Express handle this for you!
Also, if you add a property to req or res objects, it will be present in the next middlewares
For example:
router.get('/', (req,res,next) => {
req.customProperty = 'hello!';
return next();
}, (req, res) => {
console.log(req.myCustomProperty)
// hello!
})