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()
Related
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
}
I am new to NodeJs coming from Java background, I'm trying to understand the use of the convention next() call.
I've gone through the following posts -
1. javascript node.js next()
2. https://www.naeemrana.com/node-js/express-js-middleware-what-is-next/
3. When to use next() and return next() in Node.js
What I could make of them is next() is similar to a callback or maybe a reference of the next function to be executed after the current function's execution,
Also understood it's recommended to use return next(); instead of next(); to avoid the function to be re-executed.
Following is a sample code from one the links -
app.get('/users/:id', function(req, res) {
var user_id = req.params.id;
if(user_id) {
// do something
} else {
next(); // here comes middleware.
}
});
What I don't understand here is the function being executed does not have the 3rd argument,
but it does invoke the next() function, what is the idea here?
Is the use of this convention only to be used on routes logic?
Is next() a callback? is it a reference to the next function to be executed? or something else?
Node.Js doesn't know next. It's a pattern used by the express framework.
As express has a concept of middlewares that can be called in each and every request coming into it.
If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging.
var app = express()
app.use(function (req, res, next) {
if(condition_to_return_from_here){
res.end(req.params.id)
}else{
console.log('Time:', Date.now())
next()
}
})
More on expressjs and middleware - https://expressjs.com/en/guide/using-middleware.html
next() is not a nodejs thing but a express middleware function. Calling next() basically moves to the next middleware function. express is a web application for node.
You can read more https://expressjs.com/en/guide/using-middleware.html
Also, read through Express next function, what is it really for? which should you information.
next() is actually callback argument to the middleware function, called "next" by convention. When you use next parameter(as a parameter on middleware function) and then call next(), program moves to the next middleware function.
However express is saying "Notice the call above to next(). Calling this function invokes the next middleware function in the app. The next() function is not a part of the Node.js or Express API, but is the third argument that is passed to the middleware function."
So, I understand next() is not Nodejs thing. But also, next() is not express thing, I think.
you can read more: https://expressjs.com/en/guide/writing-middleware.html
Recently I changed my code from Express to Restify. I'm honestly not sure if it used to happen before, but I guess it did.
Basically in my middleware I call a promisified method and when it resolves I call next and do other stuff in the next middleware. When it is rejected I also want to call next with no errors in some cases. Otherwise it must call the error middleware passing err to next.
somePromise()
.then(()=>{
next();
})
.catch((err)=>{
if(err.someatt) next();
else next(err)
});
It works fine with the expected results of somePromise. The problem is that next is bound by the then-catch chain. And when an error is thrown in the next middleware it invokes the catch method and calls next again!
I found out that next has an attribute called and when I turn it to false before calling next again I get rid of the the errors. But of course it is an antipattern. And I'm having the same problem in a different middleware that I also used promises (calling next as expected and then calling it again in the catch statement).
Anyone else had a problem like that?
Change your chain to this:
somePromise().then(() => {
next();
}, err => {
// error occurred in somePromise()
if(err.someatt) next();
else next(err);
}).catch(err => {
// error occurred in .then()'s next()
// don't call next() again
});
The optional second argument of .then() acts as a .catch() callback, but is only invoked for errors thrown higher up in the chain, and is not invoked for errors thrown in the adjacent .then() callback.
A very helpful flowchart borrowed from this awesome answer demonstrates the difference between .then(onFulfilled, onRejected) and .then(onFulfilled).catch(onRejected):
I'm new to Node and Express and the other layers that go along with building web apps with node and the request and response parameters are really confusing me. My confusion lies in the fact that those two parameters are often present in a function but oftentimes one or both of them isn't declared. Also, much of the time an additional parameter will be thrown in, like 'next' or something else. For example, I have the following router for an API:
router.route('/teams')
// create a team at POST http://localhost:8080/api/teams
.post(function(req, res) {
var team = new Team();
team.name = req.body.name;
team.location = req.body.location;
// save the new team and check for errors
team.save(function(err) {
if (err) {
res.send(err);
};
res.json({ message: 'Team created!' });
});
})
// GET all the teams at GET http://localhost:8080/api/teams
.get(function(req, res) {
Team.find(function(err, teams){
if (err) {
res.send(err);
};
res.json(teams);
});
});
Both .post and .get call a function with req and res as parameters, but req is never used. So how does the function know what to do with req or res if they're not defined and used or not used in completely different orders? Or if I named them something completely different?
What exactly is happening with requests and responses? Sorry for my ignorance. I've read the documentation but it's not clicking.
Thanks.
When you use expressApp.use('/some/route', myRouteHandler); Express will listen for requests for that route, and when it's hit, it will call the function you provided (callback). It will give it three parameters: request and response, and next. (Actually could be four, but lets keep things simple.)
So, your callback might be defined like this:
function myRouteHandler(a, b, c) {
// do stuff
};
or like this:
function myRouteHandler(req, res, next) {
// stuff
}
or simply:
function myRouteHandler() {
// stuff
}
Whatever you do, doesn't matter. When the app is started, express listens for requests.
When one of them matches the route (/some/route), express will, in its internal workings, call the function you provided, like this:
myRouteHandler(requestObject, responseObject, nextMiddleware);
So in the first case, you can access the request (like, request headers, full url, caller IP address or similar) by using req. In your second case, you'll access it by calling a. In the third case, you can use arguments[0].
By convention, people will use the form: myCallback(req, res) and know that Express will put the request object as the first param, and response as the second. The response object actually has a method end(), so you can end the request. If there is also a next() object, you can call the next middleware.
Say you have a route defined like this:
app.use('/api/users', checkAuthorizationHandler);
app.use('/api/users', makeSureTheIPisFromOurInternalNetwork);
app.use('/api/users', nowHandleTheResponse);
Each of those handlers gets a third param. If you name it, you'd usually in your function declaration call it 'next' parameter. It means, the next function in order.
Say your function checkAuthorizationHandler(req, res, next) will check for req.headers('auth') token and if it's ok, it will in the function body, call next().
Then the function makeSureTheIPisFromOurInternalNetwork(a, b, c) is called. It will check that the a.ip is a LAN ip address and call c();
Finally your function nowHandleTheResponse() will find all users, and respond with a JSON object of the users: arguments[1].json([user1, user2, user3]);
So, first param is something that express gives you, it's the request, second is response, third is a next middleware function in line. No matter what you call them, they are there.
P.S. You can also declare your middleware with four params:
function(error, req, res, next);
Express will actually check your function and if it finds that you have four params and not two or three, it will give you any errors thrown by the middleware that ran earlier in the chain. Meaning, if your checkAuthHandler says next(new Error('Not authorized'));, your next function might check for that error, and if it's present, not give results. Often however will the middleware which detects errors just res.end('some error message');
If I haven't confused you enough, just say, I've got more where this came from :)
It is the framework convention. The first argument is the request, the second is the response. If you're declaring a middleware (.use), the third argument is the next middleware in the chain.
You can name these variables however you want, as long as you know the order. You could have something like: .post(function(a,b) {}); and then the request is represented by variable a, and response by variable b.
If, for whatever reason, you don't need the request, only the response, you still have to have a first argument, as the response is represented by the second argument.
In javascript, there's no method overload like in Java, for example (maybe here's where you getting the confusion from). A function is represented by its name, not how many arguments it takes. Here's a simple example:
function logToConsole(level, message) {
if (!message) {
message = level;
level = 'INFO';
}
console.log('['+level+']', message);
}
logToConsole('My message'); // prints out: "[INFO] My message"
logToConsole('WARN', 'My message'); // prints out: "[WARN] My message"
Did you notice how we defined a default value for level, based on the existence of message?
Hope this clarifies things a bit.
Request, response and next are passed to all middleware functions. The request object contains information about the HTTP request, and the response object is used to handle the request. The Expressjs documentation details these objects. The next() call is used in something called the dispatcher, a middleware function may or may not call next() depending on usage. Next simply calls the following middleware.
Here is an example of using next():
function helloWorld(req,res,next){
console.log('Hello, world!');
next();
}
// This function doesn't call next(), therefore it will
// not call the subsequent middleware in the dispatcher
function goodbyeWorld(req,res,next){
console.log('Goodbye, world!');
}
app.use(helloWorld);
app.use(goodbyeWorld);
Output:
Hello, world!
Goodbye, world!
Now let's reorder the middleware
app.use(goodbyeWorld);
app.use(helloWorld);
Output:
Goodbye, world!
The helloWorld function is not called. Notice the importance of middleware order and the next() function call.
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.