exit from express middleware with specific http status - javascript

Hopefully this is a simple one, but I have some custom middleware which I want to return a 404 or 401 etc to the user and stop the propagation of other handlers etc.
I was expecting I could do something like:
function SomeMiddleware(req, res, next) {
if(user.notRealOrSomething)
{ throw new HttpException(401, "Tough luck buddy"); }
return next();
}
However cannot find any specific info about how is best to do this.

You are suppose to pass errors to the next() function.
function SomeMiddleware(req, res, next) {
if(user.notRealOrSomething) {
return next(throw new HttpException(401, "Tough luck buddy"));
}
next();
}
Any argument you pass to next will be considered an error except 'route' which will skip to the next route.
When next is called with an error the error middleware will be execute.
function (err, req, res, next) {
// err === your HttpException
}
Express.js will treat any middleware with 4 arguments as error middleware.
Error-handling middleware are defined just like regular middleware, however must be defined with an arity of 4, that is the signature (err, req, res, next):
All of this is pretty well documented at: http://expressjs.com/guide/error-handling.html

Nothing prevents you from using the response object:
res.send(401, "Tough luck buddy");
next('invalid user');

The code as written in the answer should work just fine, since the code is synchronous - that is, it's not async and it doesn't hand next() off to a callback anywhere. Per the Express.js docs (emphasis mine):
Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it.
So in synchronous middlewares and handlers, just throw the error, Express will catch it and pass it to the error middleware.
For asynchronous middlewares, you need to pass the error object to the next() function without throwing it. The example from the docs applies just as well to middlewares as it does to route handlers (which are in fact also middleware), to adapt it to your example:
function SomeMiddleware(req, res, next) {
user.checkRealOrSomethingAsync(isReal => {
if(!isReal) {
// Note: Pass the exception, do not throw it!
next(new HttpException(401, "Tough luck buddy"))
}
next()
})
}
Note that, as per the Express docs, this means you must catch any errors thrown in async middlewares or routes and pass them into next().
However! Starting in Express 5, you don't need to catch errors thrown within async methods, the error will be propagated as normal without you doing anything. express-async-errors package will patch Express.js 4 to work similarly. Note this only applies to errors at the "top level" of the async function - if you're mixing async and callbacks, you'll still have to catch errors inside the callbacks.

Related

In express.js, when to use a middleware and when to use a regular function?

I am making a login authentication module using a regular function in my project. However, I see many developers online using middleware to achieve the same purpose. I am so confused about when to use middleware and when to use a normal function. In general, when is it appropriate to use middleware, does it somehow related to some specific scenarios? I want to know if there is any pattern so that I can establish good coding practices.
I tried to make the same authentication module by using middlewares and it behaves exactly the same as expected. I can always pass the (req, res) parameters to a regular function, so why don't I just use a regular function?
Middlewares are useful when you want to apply the function to an entire subset of routes. For example, if you have a dashboard with many routes (/profile, /orders, /prices), and they all require the user to be logged in to view, it would be wise to apply the middleware to the routes, so you won't have to re-write the function in all routes.
I can always pass the (req, res) parameters to a regular function, so why don't I just use a regular function?
You totally can. But middleware is actually less code. I'm thinking in terms of the below example:
// middleware example
const authMiddleware = async (req,res,next) => {
try {
... do some stuff
next()
} catch(err) {
// handle specific errors, 401, 403, etc
next(err)
}
}
const handler = (req,res) => {
... handle the request
}
app.use('path',authMiddleware,handler)
and this:
// function example
const authFn = async (req,res) => {
// ... do some stuff without calling next
}
const handler = async (req,res,next) => {
try {
await authFn(req,res)
} catch(err) {
// handle specific errors, 401, 403, etc
return next(err)
}
... handle the request
}
app.use('path',handler)
In the above example, when using a function you'd have to handle errors in each authenticated route (there's probably more than one) whereas with middleware you can just write it once and use it on all your authenticated routes.
Middleware gets to be an even better separator of concerns when you do stuff like this:
const requirePermissions = (requiredPermissions) => async (req,res,next) => {
try {
// evaluate the user against the required permissions
next()
} catch(err) {
next(err)
}
}
app.use('/somePath', requirePermissions(perm1,perm2), handler)
app.use('/someOtherPath', requirePermissions(perm3,perm4), handler)
As it makes your middleware even more reusable among routes.
Middleware's awesome.

Node ExpressJS: middleware URL route response vs route destination

router.use((req, res, next) => {
if(req.originalUrl == '/api/test'){
//does stuff
res.send(result);
}
next();
})
vs
route.get('/api/test', (req, res) => {
//does stuff
res.send(result)
}
I'm rather unfamiliar with the whole HTTP web application safety conduct so I need to ask, is there any vulnerability or downside if I use the first approach to resolve some of the route destination?
I'm rather unfamiliar with the whole HTTP web application safety conduct so I need to ask, is there any vulnerability or downside if I use the first approach to resolve some of the route destination?
There are two main differences between router.use() and router.get() and one is somewhat relevant here:
router.use() matches ANY http verb such as GET, POST, PUT, OPTION, PATCH, etc... whereas router.get() only matches GET requests, router.post() only matches POST requests, etc..
router.use() uses a "loose" match algorithm where the requested route only has to start with the path designation on the route, not match it entirely.
For the first point, your middleware handler is doing res.send(response) for all http verbs that have a request path of /api/test. That is probably not what you want and is not how you should write the code. You should have your code respond only to the http verbs for that path that you actually intend to support and do something useful with. Other verbs should probably respond with a 4xx status code (which would be the default in Express if you don't have a handler for them).
For the second point, your middleware handler is generic (no path set) and you are already checking for the entire path so that point is not relevant.
With one small addition, I'd say that your middleware approach is fine. I'd add a check for the req.method:
router.use((req, res, next) => {
if(req.method === "GET" && req.originalUrl == '/api/test'){
//does stuff
res.send(result);
// return so the rest of the route handler is not executed
// and we don't call next()
return;
}
next();
});
All that said, you can also probably solve your problem in a bit more generic way. For example, if you put a specific route definition in front of this middleware, then it will automatically be exempted from the middleware as it will get processed before the middleware gets to run. Routes in Express are matched and run in the order they are declared.
router.get('/api/test', (req, res) => {
//does stuff
res.send(result)
});
router.use((req, res, next) => {
// do some middleware prep work on all requests that get here
// ...
next();
});
// other route definitions here that can use the results of the prior middleware
For example, this is very common if you have some routes that need authentication and others that do not. You put the non-authenticated routes first, then place the authentication middleware, then define the routes that want to be behind the authentication check.
There is no "exact" vulnerability, but many, many drawbacks. The major difference here is that the first piece of code is what we call "a global handler". It is executed on each and every request. The second piece is just a specific route.
When you make a request, express starts to evaluate the pipeline of things it needs to do in order to return a response. First it executes all global handlers (like the first example). Then it starts matching the route against a list of handlers, and if it finds it - it executes the function.
What you risk using the first approach is breaking the chain and not executing all global/local handlers properly. Some of those might do specific things (like preventing you from some type of attacks). Then, using the second approach, you define way more things than just a global handler: you define the endpoint, as well as the method (in your case it's a GET request).
When the matcher finds your route, it does break the chain automatically for you (in the simplest scenario).
Also note that in the first example, you'd have a single point with tons of if-else statements to figure out what you want to do with this request. It basically mitigates the need of express whatsoever...
Express is made in a way that it supports multiple "middlewares". If you want to do specific pieces based on some action, here's what you can do:
router.get('/users',
handler1(req, res, next) {
if (req.something) {
next(); // we skip the logic here, but we go to handler2
} else {
// do your magic
res.send(response);
}
},
handler2(req, res, next) {
// you skipped the action from handler1
res.send(anotherResponse); // you MUST send a response in one handler
}

Any reason not to pass request / response as a parameter?

In express I have a handler for a route ex:
router.get(`${api}/path/:params/entrypoint`, routeHandler);
In this example 'routeHandler' function has a lot of logic doing various things. I'd like to break 'routeHandler' into smaller methods to ease readability and testability. So instead of:
routeHandler(req, res) {
//many lines of code
}
We could have:
routeHandler(req, res) {
helperOne(req, res);
helperTwo(req, res);
}
helperOne(req, res) {
//do stuff
}
helper2(req, res) {
//do stuff
}
I am being told not to do this by a coworker who is pretty senior, but I do not understand why. Does anyone know of any issues that can arise by passing the response or request objects into helpers? I can not think of any and google isn't revealing any clear answer.
Thanks!
Does anyone know of any issues that can arise by passing the response or request objects into helpers?
Yes you may run into some problems when passing those parameters, especially res. For example you may res.send multiple times (one in each function) which will raise an exception.
Scenario
A more concrete example is this
routeHandler((req, res) => {
helperOne(req, res);
helperTwo(req, res);
});
Based on some conditions, I want to stop and return an error from helperOne and not go execute any code from helperTwo. My definitions of these functions are like this
helperOne = (req, res) => {
const dataPoint = req.body.dataPoint; // a number for example
if (number > 10) {
return res.send("This is not valid. Stopping here...");
} else {
console.log("All good! Continue..");
}
}
helperTwo = (req, res) => {
res.send("Response from helperTwo");
}
Then let's say I do have req.body.dataPoint = 10, and I'm now expecting my routeHandler to stop after the return res.send in the first block of my if statement in helperOne.
This will not work as expected though, because the return will concern only helperOne which is the returning function. In other terms it won't propagate to routeHandler.
In the end an exception will be raised because routeHandler will call helperTwo and try to send a response again.
Solution
Don't send req or res. Just pass the data you need and handle the reponse in your main handler
An even better alternative is to use Express middlewares. Since you have multiple "sequential" handlers, you can chain multiple middlewares, which is closer to the standard Express.JS way
One reason to avoid doing this is that you're tightly coupling your helper functions to routeHandler, and encouraging complexity in the helpers. If you break up your helper functions so they only have a single responsibility, it's likely you'll only need to pass in a subset of the request.
Why are you passing in res, Are you sending a response from inside the helpers? Without knowing the details of your routeHandler implementation, I would see if you could handle logic in the helpers, but have them each return a value and keep the response-sending in the main routeHandler function. Here's a simple example:
handleRoute('/users/:userID', (req, res) => {
const { userID } = req.params;
const idIsValid = validateUserID(userID);
if (!idIsValid) {
return res.status(400).send('Invalid user ID!');
}
...
});

How to use an Express Error middleware for my case?

I'm using a router middleware to check if the request is valid or not. Also, I'm using a global Error handler middleware at the end of the server.js to catch and process all the errors.
Inside the router, I'm facing a problem implementing this. The following code will state the problem clearly.
Error middleware errorHandler.js
module.exports = function(err, req, res, next) {
//some logic
return res.status(400 or 500).json({err});
};
My server.js (short version)
const express = require('express');
const app = express();
app.use('/account/:accid/users', userRouteHandler);
const errorHandler = require('PATH to errorHandler.js file');
app.use(errorHandler);
app.listen(3000, () => console.log(`Example app listening on port 3000!`));
userRouteHandler file
var express = require('express');
var router = express.Router({ mergeParams: true });
router.use(express.json());
// middleware to use for all requests
router.use(function(req, res, next) {
/**** PROBLEM / CONFUSION ****/
checkIfAccountIdExists(req.params.accid) //if exists resolve(count) else reject(new Error)
.then(next) //I want to allow calling the get if it exists
.catch(next); //for error it should call the global error handler
});
router
.get('/', function(req,res,next){
console.log("entering here");
findAllTheUsers({accid:req.params.accid})
.then(users=>res.status(200).json(users);
.catch(err=>next(err)); //ensure it can call the global error handler
});
The code always calling the get route. As for both, I'm calling next(). If I call next() only inside then(), my global error handler will get skipped if any error occurs.
One way could be directly calling my errorHanler function within the catch block. but I want to keep my code separate and don't really want to require my errorHandler file within each route.
How can I achieve this?
Without knowing the behavior of your checkIfAccountIdExists function, my supposition is that it returns a promise that always resolves with false|undefined|null; something like this:
checkIfAccountIdExists(id).then((exists) => console.log(exists));
// outputs "false" or "undefined" or "null"
It's my supposition, because otherwise your .get('/') route shouldn't even enter, given how next() works.
Understanding next():
Calling next() (to the shame of Express) has always been confusing without having in-depth knowledge of it. It basically works in 3 ways:
next() (no arguments) -> pass execution to next callbacks in the
route
next('route') (the string 'route' argument) -> bypass any remaining callbacks in the route (moves into any routes that follow)
next(err) (any other truthy parameters, aside from 'route') -> invokes the error handlers.
In your specific case, my assumption is that checkIfAccountIdExists() solves with false|undefined|null, essentially invoking next(err) signature, but because err is not truthy, it's treated as a next() signature, moving onto the next route callback. I'd check the sanity of checkIfAccountIdExists() for this.
Using next() with Promises
When using Promises, it's important to remember that your first argument of .then() (meaning your fulfillment handler) will always receive a parameter!
promise.then(callback);
function callback() {
console.log(arguments.length); // ALWAYS 1
}
For this reason, you should always avoid setting next() as a fulfillment handler to promises. Otherwise, once your checkIfAccountIdExists() will solve with true, it will actually invoke the next(err) signature!
Always write: promise.then(() => next()) instead of promise.then(next), to make sure you call next without arguments.
Writing promise.catch(next) is however fine, because it's the same as promise.catch((err) => next(err))
A bit more info on Promises
Also, Promises (thenables) allow for two arguments on .then(), one being a fulfillment handler and one a rejection handler.
Example: promise.then(onFulfillment, onRejection) which is similar to calling promise.then(onFulfillment).catch(onRejection) except of how errors are caught!
For .then(onFulfillment, onRejection) any errors that are thrown
inside onFulfillment are never caught by onRejection.
For .then(onFulfillment).catch(onRejection) any errors that are thrown
inside onFulfillment are also caught by onRejection
In your case, that means you can safely write
checkIfAccountIdExists.then(() => next(), next);
because the next route (onFulfillment arg) will also handle errors.
Note: errors inside synchronous code will be caught by express.
More info:
Express Error Handling
Promise.prototype.then()

Catching unhandled exceptions in Express when using async/await

Regard the following TypeScript code:
app.get('/test_feature', function (req: Request, res: Response) {
throw new Error("This is the bug");
});
app.use(logErrors);
function logErrors (err: Error, req: Request, res: Response, next: NextFunction) {
console.log(err);
mongoDal.log(err.message, err);
next(err);
}
Here, I throw an error in a requests handler, and it fires the logErrors function as expected.
But then, I change my code to consume an async function:
app.get('/test_feature', async function (req: Request, res: Response) {
throw new Error("This is the bug");
await someAsyncFunction();
});
Now, because my function is async, the error somehow gets handled by the default error handler of Express, so my custom error handler doesn't get reached, nor the Node default error handler:
process.on('uncaughtException', function (err: Error) {
try {
console.log(err);
mongoDal.log(err.message, err);
} catch (err) {
}
});
How can I make my 'logErrors' function reached when an error occurs in an async function? I want a generic solution, not to try/catch in every requests handler.
The problem here is that your handler isn't throwing a synchronous exception at all any more. Instead, your handler returns a promise, which gets rejected. Note that this isn't a promise or async/await specific problem, this is a general issue for any express code using callbacks etc too - if you don't handle errors carefully everywhere when writing async code, it's easy to lose them completely.
To handle this in your case, something needs to register itself to catch rejections from the promise that you're returning. There's a few options for that:
Explicitly add a .catch() to all your error handlers, and handle errors yourself, or by calling next(err) to delegate to the normal express error handling.
Create a wrapping function for your handler to do this, and use it everywhere. You could use an existing wrap() function like express-promise-wrap for this.
Extend .get and friends to automatically track rejections in promises returned from handlers. You could do this by hand, but it looks like express-as-promised is a working implementation of this (although I haven't tried it).
It's a little more complicated to set up, but 3 is strongly preferably in my opinion once you've got it in place. With that, you should be able to just write async functions as handlers, they'll all be returning promises under the hood, and your handler code will automatically monitor those promises for any subsequent failure.
StrongLoop have actually got an article looking at this generally in more detail, if you want some further reading: https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/

Categories