How does the return of control flow work in a chain of Express middleware functions? - express

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!
})

Related

How to pass arguments to a callback function in express?

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.

app.get with multiple arguments - e.g. app.get('/', requireAuth, (req, res) => { })

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.

Using Node.js / Express is it possible to next() an error from inside an IIFE and advance to the error handling middleware?

Question: Using Express is it possible to return an error from inside an IIFE and advance to my error handling middleware?
Background: The IIFE is used to create an async container to wrap await statements. I can't see a way out of this and I wonder if I'm using the wrong basic, pattern altogether.
Simplified Example:
app.get('/', function(req, res, next) {
(async function() {
try {
let example = await someLogic(x);
} catch(err) {
return next(new Error('oops'));
}
})();
console.log('main endpoint');
});
app.use(function(err, req, res, next) {
console.log('my error', err.message);
});
Using Express is it possible to return an error from inside an IIFE and advance to my error handling middleware?
Yes, that works fine. It will call next(err) just fine. But, your return will return only from the IIFE and the rest of your request handler after the try/catch will still execute (not sure if you want that or not).
FYI, it's probably simpler to declare the request handler as async and then you don't need the IIFE wrapper:
app.get('/', async function(req, res, next) {
try {
let example = await someLogic(x);
console.log('main endpoint');
// send some response here
} catch(err) {
return next(new Error('oops'));
}
});

Express all issue

I docs it said that this two way of defining two sequential midlewares are equivalent
http://expressjs.com/4x/api.html#app.all
app.all('*', requireAuthentication, loadUser);
Or the equivalent:
app.all('*', requireAuthentication)
app.all('*', loadUser);
But it if we have middlewares defined as:
var requireAuthentication = function(req, res, next){
req.params.someParam = 1
next()
}
var loadUser = function(req, res, next){
console.log('someParam', req.params.someParam)
...
}
In first case req.params.someParam inside loadUser will be 1, and in second case it will be undefined.
Is it a bug or wat?
In short: req.params doesn't persist across routes. Use res.locals instead.
Not a bug in Express, but not very intuitive. This is a good question!
req.params is for grabbing parameters out of a URL. For example, if you had a route defined like this...
app.get('/user/:id', function(req, res) {
res.send('user id == ' + req.params.id);
});
...and you visited /user/123, then req.params.id would be "123".
This params property is different for every route. Take a look at this example:
app.get('/user/:userid', function(req, res, next) {
// req.params.userid is defined in here
// req.params.id is NOT defined here
next();
});
app.get('/user/:id', function(req, res) {
// req.params.id is defined in here
// req.params.userid is NOT defined here
});
req.params isn't the same for every route, which explains what you're seeing.
Your first example has one route with two request handler functions, so they all have access to the same req.params object. Your second example has two routes with two request handler functions, so they're accessing different req.params objects.
If you're trying to persist data between different request handler functions across the same request, you should use res.locals. To rewrite your example:
var requireAuthentication = function(req, res, next){
res.locals.someParam = 1
next()
}
var loadUser = function(req, res, next){
console.log('someParam', res.locals.someParam)
...
}
Using res.locals will work whether you have one line or two.
Hope that helps!

Sending 405 from express.js when there is a route match but no HTTP method match

I'm looking for a clean way to have my express app return 405 Method Not Allowed if a client sends a request that matches a mapped url route but does not match the mapped HTTP method.
My current implementation is to have a default "catch-all" handler that tries to match the url against the register routes, ignoring the HTTP method. If there is a match, then we know to return a 405, otherwise we let express do its default 404 behavior.
I'm hoping there is a better way that doesn't involve running all the route matching twice (once by express, once by my handler).
Here is an approach that I have used successfully with multiple Django applications and now with Node and Express. It is also follows RFC 2616 (HTTP/1.1) that says the following about HTTP 405:
The response MUST include an Allow header containing a list of valid
methods for the requested resource.
So, the key point is to route the requests to the same handler without regard to methods.
app.all('/page/:id', page.page);
app.all('/page/:id/comments', page.comments);
app.all('/page/:id/attachments', page.attachments);
...
The next point is to validate the method in the handler function 'comments'. Note that the handler is responsible for handling all the methods. In Django's world this is the only way to go because the framework forces you to separate the routing of the URLs from the actual action about to be performed against the resource the URL represents.
In the handler you could check the method like this...
exports.comments = function (req, res) {
if (req.route.method === 'get') {
res.send(200, 'Hello universe.');
} else {
res.set('Allow', 'GET');
res.send(405, 'Method Not Allowed');
}
}
...but as you can expect the code will quickly become repetitious and not nice to read especially when you have many handler functions and many different sets of allowed methods.
Therefore I prepared a shortcut function named restful for the job. Define the function wherever you want. I personally would place it in helpers.js under the same directory where the handler functions are implemented.
var restful = function (req, res, handlers) {
//
// This shortcut function responses with HTTP 405
// to the requests having a method that does not
// have corresponding request handler. For example
// if a resource allows only GET and POST requests
// then PUT, DELETE, etc requests will be responsed
// with the 405. HTTP 405 is required to have Allow
// header set to a list of allowed methods so in
// this case the response has "Allow: GET, POST" in
// its headers [1].
//
// Example usage
//
// A handler that allows only GET requests and returns
//
// exports.myrestfulhandler = function (req, res) {
// restful(req, res, {
// get: function (req, res) {
// res.send(200, 'Hello restful world.');
// }
// });
// }
//
// References
//
// [1] RFC-2616, 10.4.6 405 Method Not Allowed
// https://www.rfc-editor.org/rfc/rfc2616#page-66
//
// [2] Express.js request method
// http://expressjs.com/api.html#req.route
//
var method = req.route.method; // [2]
if (!(method in handlers)) {
res.set('Allow', Object.keys(handlers).join(', ').toUpperCase());
res.send(405);
} else {
handlers[method](req, res);
}
}
With restful it is now quite painless to handle 405 responses automatically and having proper Allow header being set. Just give a function for each method you allow and restful does the rest.
So lets modify the previous example:
exports.comments = function (req, res) {
restful(req, res, {
get: function (req, res) {
res.send(200, 'Hello restful universe.');
}
});
}
Why the name restful? In RESTful web it is quite essential for the API to obey the conventions like responsing with HTTP 405 to the request having non-supported method. Many of those conventions could be integrated to restful when needed. Therefore the name is restful and not something like auto405 or http405handler.
Hope this helps. Any thoughts?
Method 1: Use .route() and .all()
// Your route handlers
const handlers = require(`./handlers.js`);
// The 405 handler
const methodNotAllowed = (req, res, next) => res.status(405).send();
router
.route(`/products`)
.get(handlers.getProduct)
.put(handlers.addProduct)
.all(methodNotAllowed);
This works because requests are passed to the handlers in the order they are attached to the route (the request "waterfall"). The .get() and .put() handlers will catch GET and PUT requests, and the rest will fall through to the .all() handler.
Method 2: Middleware
Create middleware which checks for allowed methods, and returns a 405 error if the method is not whitelisted. This approach is nice because it allows you to see and set the allowed methods for each route along with the route itself.
Here's the methods.js middleware:
const methods = (methods = ['GET']) => (req, res, next) => {
if (methods.includes(req.method)) return next();
res.error(405, `The ${req.method} method for the "${req.originalUrl}" route is not supported.`);
};
module.exports = methods;
You would then use the methods middleware in your routes like this:
const handlers = require(`./handlers.js`); // route handlers
const methods = require(`./methods.js`); // methods middleware
// allows only GET or PUT requests
router.all(`/products`, methods([`GET`, `PUT`]), handlers.products);
// defaults to allowing GET requests only
router.all(`/products`, methods(), handlers.products);
Due to ambiguity, there really is no other way. Personally, I would do something like this:
var route = '/page/:id/comments'
app.get(route, getComments)
app.all(route, send405)
function send405(req, res, next) {
var err = new Error()
err.status = 405
next(err)
}
Either way, you have to check the routes twice.
Kinda old question but here is what i did. I just put this after all my routes but before my 400 handler
// Handle 405 errors
app.use(function(req, res, next) {
var flag = false;
for (var i = 0; i < req.route.stack.length; i++) {
if (req.method == req.route.stack[i].method) {
flag = true;
}
}
if (!flag) {
err = new Error('Method Not Allowed')
err.status = 405;
return next(err)
}
next();
});
I have been doing it this way:
Say if you have GET and POST method handlers for /. You can wrap the path with app.route or router.route and assign the handlers accordingly.
app.route("/").get((req, res) => {
/* DO SOMETHING*/
}).post((req, res) => {
/* DO SOMETHING*/
}).all((req, res) => {
res.status(405).send();
});
http://expressjs.com/en/4x/api.html#app.route
http://expressjs.com/en/4x/api.html#router.route
A request will get matched to the route and filtered through the handlers. If a handler is present, it will get handled as usual. Else, it will reach the all handler that will set the status code to 405 and ending the request.
I fixed it like this :
/*paths here*/
router.get('/blah/path1', blah.do_something );
router.post('/blah/path2', blah.do_something_else );
/* if we get here we haven't already gone off down another path */
router.all('/*', (req,res) => { res.status(405),
res.json({'status':405,
'message':req.method + ' not allowed on this route'})
});
/* simples */
I thought this was a pretty interesting problem and so I dove deeeeeep down into the depths of the express app function and found a way to dynamically build a 405 error that includes all of the possible routes (without having to manually update anything when you add a new route).
app.use("", (req, _, next) => {
const err = buildError(app, req);
if (!err) return next();
return next(err);
});
For those interested you can find the npm package here https://www.npmjs.com/package/express-ez-405, and below is a quick example of what it looks like to use it.
const express = require("express");
const { buildError } = require("express-ez-405");
const app = express();
app.use(express.json());
const userRouter = require("./routes/user");
const mainRouter = require("./routes/main");
const nestedRouter = require("./routes/nested");
// // Routes
app.use("/main", mainRouter);
app.use("/user", userRouter);
app.use("/nested/route", nestedRouter);
// Routes
// 405 & 404 error catcher
app.use("", (req, _, next) => {
const err = buildError(app, req);
if (!err) return next();
return next(err);
});
// 405 & 404 error catcher
// Error handling
app.use((err, _, res, __) =>
res.status(err.status).json({ message: err.message })
);
// Error handling
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}...`);
});
Once this in in there you never have to worry about updating it regardless of whether you add, remove, or change routing.