I'm creating a module, which is an express middleware. My question is, when it encounters some kind of error (for example an acl module that sees that the user has no access right to this content), what is the best practice, calling next() with an error, so the app using the module can handle it the way it chooses, or can I send a response and not even call next?
Yes, you pass a error into next function to allow user catch error in callback and do what he needs.
More you can read in this articles:
Who provides the next() function in Express middleware?
https://blog.safaribooksonline.com/2014/03/10/express-js-middleware-demystified/
Related
Is it valid to call next() in route handler?
In my app I have a middleware at the end of the stack to handle all the unidentified requests.
What I want to do is to send an error page as response when the invalid title is encountered.
The snippet in question is the following:
app.get('/:title', (req, res, next) => {
const title = req.params.title;
if (title.isValid()) {
res.render('post', { title });
} else {
next();
}
});
This is the middleware to handle the unidentified requests:
app.use((req, res, next) => {
res.status(404).render('error');
next();
});
FYI: the snippets above do work. I am just not sure if this is the right way to implement this sort of behavior.
You have basically three choices when you want to send an error response.
You can just send the error response right where the error occurs and not call next().
You can call next(err) where you create an appropriate Error object and let Express' error handling process that error and send the response (which could also be your own custom error handler that the error would flow to).
You can call next() and rely on hitting your default 404 error handler (as you show in your question). This will only work if there are no other routes that might also try to handle this request.
There are logical reasons for any of these methods, depending upon your app architecture plan and just how you want to do things. All can work.
Is it valid to call next() in route handler?
Yes, if you want to continue routing to other route handlers or you know there are no more route handlers that will match and you want it to flow to the 404 error handler.
You can use next() equally well from either middleware or a route handler. It behaves no different in either. The main difference between app.use() and app.get() is just in how it matches the route, not in what happens once the middleware/route handler executes on a matching route. They work the same in that regard.
FYI, app.get() is JUST a more specific version of app.use().
app.get() only matches the GET http verb whereas app.use() matches all http verbs.
app.get() requires a full path match, app.use() will match on a partial path match. For example the path /category/mountain will match app.use("/category", ...), but not app.get("/category", ...).
See What is the difference between a route handler and middleware function in ExpressJS? for some further explanation.
Once they've matched are are executing the code in the handler, they work identically with next() or res.send(), etc...
So, it's perfectly fine to use next() in a route handler if your desired architecture wants to do it that way. You do have to make sure you're always sending exactly one http response for an incoming request. Never failing to send a response and never sending more than one response. So, that becomes a requirement of however you choose to do things. For example, you would never call next() AND send a response in that same handler because that would likely lead to sending multiple responses.
There are even cases where you might have two matching routes for the same path and if the first one decides that it shouldn't handle the request (perhaps because of some state or some query parameter or something like that), it can call next() to allow the second one to get a chance to process that request.
My understanding is that a middleware function is a route handler with the exception that it may invoke the next function parameter to pass control on to the middleware function on the stack. Is this the only difference between a standard route handler and a middleware function?
Most of what you're talking about is semantics. In ExpressJS, middleware can be a route handler or a route handler can behave as middleware. So, there is not a hard and fast line between the two. But, when people refer to middleware or a route handler in a programming discussion, they usually mean something slightly different for each...
Middleware
As generic terms, middleware is code that examines an incoming request and prepares it for further processing by other handlers or short circuits the processing (like when it discovered the user is not authenticated yet). Some examples:
Session Management. Parse cookies, look for session cookie, lookup session state for that cookie and add session info to the request so that other handlers down the line have ready access to the session object without any additional work on their part. In express, this would be express.session().
Authentication. Check if the user is trying to access a portion of the site that requires authentication. If so, check if their authentication credentials are good. If not, send an error response and prevent further processing. If so, allow further processing.
Parsing of Cookies. Parse incoming cookies into an easy-to-use data structure a request handler can have easy access to cookie data without each having to parse them on their own. This type of middleware is built into Express and happens automatically.
Parsing and Reading of POST/PUT bodies. If the incoming request is a POST or PUT, the body of the request may contain data that is needed for processing the request and needs to be read from the incoming stream. Middleware can centralize this task reading the body, then parsing it according to the data type and putting the result into a known request parameter (in Express this would be req.body). Express has some ready-to-use middleware for this type of body parsing with express.json() or express.urlencoded(). A middleware library like multer is for handling file uploads.
Serve static files (HTML, CSS, JS, etc...). For some groups of URLs, all the server needs to do is to serve a static file (no custom content added to the file). This is common for CSS files, JS files and even some HTML files. Express provides middleware for this which is called express.static().
Route Handler
As a generic term, a route handler is code that is looking for a request to a specific incoming URL such as /login and often a specific HTTP verb such as POST and has specific code for handling that precise URL and verb. Some examples:
Serve a specific web page. Handle a browser request for a specific web page.
Handle a specific form post. For example, when the user logs into the site, a login for is submitted to the server. This would be handled by a request handler in Express such as app.post("/login", ...).
Respond to a specific API request. Suppose you had an API for a book selling web-site. You might provide in that API the ability to get info on a book by its ISBN number. So, you design an api that supports a query for a particular book such as /api/book/list/0143105426 where 0143105426 is the ISBN number for the book (a universal book identifier). In that case, you'd create a request handler in Express for a URL that looks like that: app.get('/api/book/list/:isbn', ...). The request handler in Express could then programmatically examine req.parms.isbn to get the request isbn number, look it up in the database and return the desired info on the book.
So, those are somewhat generic descriptions of middleware vs. request handlers in any web server system in any language.
In Express, there is no hard and fast distinction between the two. Someone would generally call something middleware that examines a bunch of different requests and usually prepares the request for further processing. Someone would generally call something a route handler that is targeted at a specific URL (or type of URL) and whose main purpose is to send a response back to the client for that URL.
But, the way you program Express, the distinction is pretty blurry. Express offers features for handling routes such as:
app.use()
app.get()
app.post()
app.put()
app.delete()
app.all()
Anyone of these can be used for either middleware or a route handler. Which would would call a given block of code has more to do with the general intent of the code than exactly which tools in Express it uses.
More typically, one would use app.use() for middleware and app.get() and app.post() for route handlers. But there are use cases for doing it differently than that as it really depends upon the particular situation and what you're trying to do.
You can even pass more than one handler to a given route definition where the first one is middleware and followed by a route handler.
app.get("/admin", verifyAuth, (req, res) => {
// process the /admin URL, auth is already verified
req.sendFile("...");
});
It is common for middleware to be active for a large number of different requests. For example, you might have an authentication middleware that prevents access to 95% of the site if the user isn't already logged in (say everything except the a few generally information pages such as the homepage and the login and account creation pages).
It is also common to have middleware that is active for all HTTP verbs such as GET, POST, DELETE, PUT, etc... In express, this would usually be app.use() or app.all(). Request handlers are usually only for one particular verb such as app.get() or app.post().
You might have session middleware that loads a session object (if one is available) for every single request on the site and then passes control on to other handlers that can, themselves, decide whether they need to access the session object or not.
It is common for request handlers to be targeted at a specific URL and only active for that specific URL. For example, the /login URL would typically have one request handler that renders that particular page or responds to login form requests.
Path Matching for app.use() is Different
In Express, there's one other subtle difference. Middleware is typically specified with:
app.use(path, handler);
And, a route is typically specified with:
app.get(path, handler);
app.post(path, handler);
app.put(path, handler);
// etc...
app.use() is slightly more greedy than app.get() and the others in how it matches the path. app.get() requires a full match. app.use() is OK with a partial match. Here are some examples:
So, for a URL request /category:
app.use("/category", ...) matches
app.get("/category", ...) matches
For a URL request /category/fiction:
app.use("/category", ...) matches
app.get("/category", ...) does not match
You can see that app.use() accepts a partial URL match, app.get() and it's other cousins do not accept a partial URL match.
Now, of course, you can use app.get() for middleware if you want and can use app.use() for request handlers if you want, but typically one would use app.use() for middleware and app.get() and its cousins for request handlers.
Ok, let's explore some definitions first.
Middleware function
definition from the express documentation:
Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle. The next function is a function in the Express router which, when invoked, executes the middleware succeeding the current middleware.
Handler function or callback function
definition from express Documentation:
These routing methods specify a callback function (sometimes called “handler functions”) called when the application receives a request to the specified route (endpoint) and HTTP method. In other words, the application “listens” for requests that match the specified route(s) and method(s), and when it detects a match, it calls the specified callback function.
Here Routing methods are the methods derived from HTTP requests and the all() method that matches all the HTTP methods. But remind, the use() method is not a routing method. Let's go to express documentation for clarification.
Routing Methods
From express documentation:
A route method is derived from one of the HTTP methods and is attached to an instance of the express class. Express supports methods that correspond to all HTTP request methods: get, post, and so on. There is a special routing method, app.all(), used to load middleware functions at a path for all HTTP request methods.
So we see that middleware functions and handler functions are not opposite of each other.
Middleware functions are functions that take an extra argument, next along with req and res which is used to invoke the next middleware function.
app.use() and the other HTTP derived methods(app.get(), app.all() etc) can use middleware functions. The difference between handler function and middleware function is same on both app and router object.
On the other hand, a handler function is a function that is specified by the routing methods.
So when any of the routing methods pass a function that has req, res, and next then it is both a middleware function and a handler function.
But for app.use() if the function has req, res, and next, then it is only a middleware function.
Now, what about the functions which only have req and res don't have the next argument?!
They are not middleware functions by definition.
If such function is used on routing methods then they are only handler functions. We use such a handler function which is not a middleware when it is the only one callback function. Because in such cases there is no need for next which calls the next middleware function.
If they are used on app.use() then they are not middleware, nor handler function.
Ok, enough of the definitions, But is there any difference between middleware functions of app.use() and handler & middleware functions of routing methods??
They look similar since both have the next argument and works almost the same. But there is a subtle difference.
The next('route') works only on handler & middleware functions. So app.use() can not invoke next('route') since they can have only middleware functions.
According to express Documentation:
next('route') will work only in middleware functions that were loaded by using the app.METHOD() or router.METHOD() functions.
METHOD is the HTTP method of the request that the middleware function handles (such as GET, PUT, or POST) in lowercase.
If you know what is next('route') then the answer is finished here for you :).
In case you don't, you can come along.
next('route')
So let's see what is next('route').
From here we will use the keyword METHOD instead of get, post, all, etc.
From the previous middleware function definition, we see app.use() or app.METHOD() can take several middleware functions.
From the express documentation:
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.
We see each middleware functions have to either call the next middleware function or end the response.
But sometimes in some conditions, you may want to skip all the next middleware functions for the current route but also don't want to end the response right now. Because maybe there are other routes which should be matched. So to skip all the middleware functions of the current route without ending the response, you can run next('route'). It will skip all the callback functions of the current route and search to match the next routes.
For Example (From express documentation):
app.get('/user/:id', function (req, res, next) {
// if the user ID is 0, skip to the next route
if (req.params.id === '0') next('route')
// otherwise pass the control to the next middleware function in this stack
else next()
}, function (req, res, next) {
// send a regular response
res.send('regular')
})
// handler for the /user/:id path, which sends a special response
app.get('/user/:id', function (req, res, next) {
res.send('special')
})
See, here in a certain condition(req.params.id === '0') we want to skip the next callback function but also don't want to end the response because there is another route of the same path parameter which will be matched and that route will send a special response. (Yeah, it is valid to use the same path parameter for the same METHOD several times. In such cases, all the routes will be matched until the response ends). So in such cases, we run the next('route') and all the callback function of the current route is skipped. Here if the condition is not met then we call the next callback function.
This next('route') behavior is only possible in the app.METHOD() functions.
Recalling from express documentation:
next('route') will work only in middleware functions that were loaded by using the app.METHOD() or router.METHOD() functions.
That means this next('route') can be invoked only from handler & middleware functions. The only middleware functions of app.use() can not invoke it.
Since skipping all callback functions of the current route is not possible in app.use(), we should be careful here. We should only use the middleware functions in app.use() which need not be skipped in any condition. Because we either have to end the response or traverse all the callback functions from beginning to end, we can not skip them at all.
You may visit here for more information
I am overall clueless about how and why you set up a node.js app, and how any of the app.use functions work - the tutorials on it don't explain the why of anything.
Anyway, I have socket.io, res.locals and index.js set up like so in the app.js root file.
const sockets = require('./models/socket')(io)
app.use(function (req, res, next) {
res.locals.user_id = req.session.user_id;
next();
});
const routes = require('./routes/index');
app.use('/', routes);
I'd like to be able to access res.locals in the socket.js model, like I can in index.js found in the routes folder.
I can't guess how to go about doing this. If anybody is able to explain how and why I can or can't that would be a bonus. Thanks!
Welcome to Expressjs, there are a few fundamentals you should probably research before going any further, they'll help solve some of your confusion. I'll give a brief explanation of them but I suggest you do further research. I'll then answer your actual question at the end.
Middleware and app.use
Expressjs is built upon an idea that everything is just "middleware". Middleware is a function which runs as part of a request chain. A request chain is essentially a single client request, which then goes through a chain of a number of middleware functions until it either reaches the end of the chain, exits early by returning a response to the client, or errors.
Express middleware is a function which takes the following three arguments.
req (request) - Representing the request made by a client to your
server.
res (response) - Representing the response you will return to
the client.
next - A way of telling express that your current
middleware function is done, and it should now call the next piece of
middleware. This can either be called "empty" as next(); or with an
error next(new Error());. If it is called empty, it will trigger
the next piece of middleware, if it is called with an error then it
will call the first piece of error middleware. If next is not called at the
end of a piece of middleware, then the request is deemed finished and the
response object is sent to the user.
app.use is a way of setting middleware, this means it will run for every request (unless next() is either not called by the previous piece of middleware for some reason, or it's called with an error). This middleware will run for any HTTP request type (GET, POST, PUT, DELETE, etc).
app.use can take multiple arguments, the important ones for beginners to learn are: app.use(func) and app.use(path, func). The former sets "global" middleware which runs no matter what endpoint (url path) the client requests, the latter (with a specific path) is run only if that specific path is hit. I.e. app.use('/hello', (req, res, next) => { res.send('world'); }); will return "world" when the endpoint "/hello" is hit, but not if the client requests "/hi". Where as app.use((req, res, next) => { res.send('world'); }); would return "world" when you hit any endpoint.
There are more complex things you can do with this, but that's the basics of attaching middleware to your application. The order they are attached to the application, is the order in which they will run.
One more thing, this will blow your mind, an express application made with the standard const app = express() can also be used as middleware. This means you can create several express applications, and then mount them using app.use to a single express application. This is pretty advanced, but does allow you to do some really great things with Express.
Why can you not access res.locals in socket.io? (The real question)
Within your middleware handler, you are setting up a res.locals.use_id property. This only lives with that individual request, you can pass it around as long as the request is alive by passing it into other functions, but outside of that request it doesn't exist. res is literally the response object that tells Express how to respond to the clients request, you can set properties of it during the request but once that HTTP request has ended it's gone.
Socket.io is a way of handling web socket requests, not standard HTTP requests. Thus, in a standard express HTTP request you will not be able to hand off the connection to anything with socket.io, because the connection is a single short lived HTTP request. Likewise, you won't be able to do the same the other way.
If you wish to find the users id in a socket.io request, you'll have to do this within the socket.io request itself.
Right now, you're entering a piece of middleware for an Express.js request, you are then calling next() which runs the next piece of express middleware, at no point does it cross over into Socket.io realms. This is often confused by tutorials because Socket.io can handle requests across the same port as Express is listening on, but the two are not crossed over. So you will need to write separate middleware for both Express.js requests chains, and socket.io request chains. There are ways of writing this code once and then writing an adapter to use it across both platforms, but that's not what you've tried to do here.
I would suggest you look at doing just nodejs and express for a time before taking on socket.io as well, otherwise you're trying to learn a whole heap of technologies all at once is quite a lot to try and take on board all at once.
I have to test for server errors (Express) in acceptance tests that can't (or shouldn't) be sent with response, for example
Error: Can't set headers after they are sent.
Catching an error with error handler and responding with 5XX code would provide valuable feedback here, but the problem is that the headers have been sent already.
This kind of bugs may be noncritical and hard to spot, and usually they are figured out from the logs.
The spec is
it('should send 200', function (done) {
request(app).get('/').expect(200, done);
});
And tested app is
app.get('/', function (req, res, next) {
res.sendStatus(200);
next();
});
app.use(function (req, res) {
res.sendStatus(200);
});
What is the most appropriate way to communicate between Express app instance and request testing library (i.e. Supertest) in similar cases?
The question is not restricted to Supertest. If there are packages that can solve the problem that Supertest can't, they may be considered as well.
Try to just set the status code without send it and avoid send it twice throwing the error use res.status().
As the express documentation say it
Sets the HTTP status for the response. It is a chainable alias of
Node’s response.statusCode.
IMHO if you want to detect it in a end-to-end (E2E) testing tool like supertest (or Selenium) you have to handle the express error and send a correct output (500 status error, some message...) to permit detect it.
Or use instead a unit test, to test that the controller function doesn't throw any error using chai or a native assertion.
I answered a similar question here. The "can't set headers after they have been set" error should be raised by express as an unhandled exception. So, you should be able to get access to it via the unhandledException event that is raised by the process.
However, this is much more tricky because of the timing. Your test case's expect function and done function will be queued for processing on the event loop on the tick right after the first res.statusCode call. Unfortunately, the next call for res.statusCode can happen at an indeterminate amount of time afterwards. For example, what if the second route handler called a really slow webservice or db and then called res.statusCode.
With that in mind, your options are pretty hard. The brute force way is to wait in your test code for a determinate amount of time and then check. It's effective but slow and non-deterministic, which will cause your test to be flaky.
Another option is to check any instrumentation code that you might have in express. If you have code in express that keeps metrics of the number of in process calls for the various route handlers you could expose these metrics to your test code. Then one of your conditions for finishing your test is that all metrics for in process route calls are 0. The second option would allow you to write deterministic tests and be much faster because you could poll the metrics.
The final option would be to handle this test case through unit tests. This is probably the best solution because it would be deterministic and wouldn't require any sort of polling. However, the downside is that you need to know that both of your functions are called in order which leads you down a path of trying to recreate in your test code the logic that express uses to call route handlers.
I did this using HAPI instead of Express, but I solved the same problem. I used an external library to make the call (like request-promise) and it worked. Catch the error in the request-promise response.
After asking this question, I've found that I can add a callback array to an endpoint in a sails app like this:
file: /api/policies/somepolicy.js
module.exports = thisIsAnArrayOfCallbacks;
This works ok while each member of thisIsAnArrayOfCallbacks is a function which accepts req, res, and next as arguments. The controller call executes all the functions in the array and the expected result is obtained in a normal flow.
But when using an errorHandler callback (like the one in this example) which takes an additional err parameter, it doesn't work as expected: the express-only version app.get('/path', thisIsAnArrayOfCallbacks) allows the errorHandler to fetch the exception and report a proper response to the client, but when using the sails way, the errorHandler function isn't called and the exception is thrown in the response.
How could I fetch the err parameter or catch the exception occurred in one of the functions of thisIsAnArrayOfCallbacks to send a proper response (a custom one is preferred) to the client?
Thanks in advance.
You're correct in that policies can't be defined as error callbacks; they're solely route-handling middleware and are actually bound to each individual route that they are applied to. Ideally, you'd catch any errors within the policy functions themselves using try/catch and send a response using something like res.forbidden(), res.badRequest(), res.serverError(), etc. In Sails v0.10 you can make any custom response you want and store it in the api/responses folder.
If you really want to implement a catch-all error handler in Sails you have two choices: either override the default 500 handler (in Sails v0.10 this is in /api/responses/serverError, in v0.9.x it's config/500.js), or (in v0.10) create custom Express middleware and load it using sails.config.express.loadMiddleware. See this SO question for details on the second option, and remember to add your custom error handler after the router and before (or in place of) the 500 handler.