Is there a way to register an express.js app.get() call with a lower priority?
For example,
app.get('*', function(req, res) {});
app.get('/page', function(req, res) {});
Would there be a way to specify a lower priority on that first call so that it is at the bottom of the route lookup, allowing the later called path to be checked first, effectively as if the first line of code was executed after the second line of code?
I just had a quick look at Express' source code and it does not seem you can set any kind of priority when you add routes. Routes are always matched by order of creation. In your example, it will first try to match '*', then '/page'.
However, you can ask Express to resume matching after you are done:
app.get('*', function (req, res, next) {
// run some tests on the request
// ...
// finally, resume matching
next();
});
Related
I tried to replace the / path with another callback:
app.get('/', (req, res) => res.send('Hello World!'))
app.get('/', (req, res) => res.send('404'))
But when I navigate to / path it still responds with Hello World! instead of 404, so that means that the callback was not replaced. So is there any way to do this?
Clarification:
What I actually want to do is to delete routes at runtime, but replacing callbacks with something that respond with say 404 would also do the trick.
It's not clear from your question, but it looks like you have both routes present in your code. If that is the case, then only the first one will be used.
If you are trying to set up some sort of dynamic routes (ie. replace the route handler completely after the application starts), I'm not sure that is possible in Express. According to this comment on deleting Express routes at runtime, it appears that the routes are optimized when the application is first initialized and there is no easy way to change them after that.
If you need a route to behave differently at runtime, the way to do it is in the route callback. For example:
app.get('/', (req, res) => {
// test something & respond accordingly
if (req.query.someValue === 'do this') {
return res.send('Hello world');
}
// otherwise, return a 404 error
return res.status(404).send('No way, man!');
});
According to the docs for express, the path parameter is optional for app.use, so to apply the middleware to any incoming request you can write:
app.use(function (req, res, next) {
res.send('ANY request');
next();
});
But for app.get the path parameter is apparently not optional, so to apply the middleware to any incoming GET request you have to write:
app.get('/', function (req, res, next) {
res.send('GET request');
next();
});
But I find that it doesn't complain if I do miss out the path:
app.get(function (req, res, next) {
res.send('GET request');
next();
});
So, are the above two definitions equivalent, or is the second one doing something different to the first one?
I'm also not sure of the difference between specifying / or * as the path:
app.get('*', function (req, res, next) {
res.send('GET request');
next();
});
So, in summary, is there any difference between app.get('/', fn) and app.get('*', fn) and app.get(fn)?
Somewhat confusingly, there are two methods called app.get:
https://expressjs.com/en/4x/api.html#app.get
One is the converse to app.set, the other is the one for handling GET requests. In practice JS only allows a single method, so internally Express checks how many arguments are passed to work out which one you meant:
https://github.com/expressjs/express/blob/351396f971280ab79faddcf9782ea50f4e88358d/lib/application.js#L474
So while using app.get(fn) might not complain, it won't actually work as a route because it'll be treating it as the other form of get.
The difference between app.get('*', ...) and app.get('/', ...) is that the * will match any path whereas / will only match the exact path / (and nothing more). This is different from app.use, where the path is treated like a 'starts with'.
You may find the answer I gave here helpful to understand how paths differ between get and use: Difference between app.use and app.get *in proxying*.
Accord to a book for express3. In the following case, http://localhost:3000/abcd will always print "abc*", even though the next route also matches the pattern.
My question is work in the same way for express4 ?
app.get('/abcd', function(req, res) {
res.send('abcd');
});
app.get('/abc*', function(req, res) {
res.send('abc*');
});
Reversing the order will make it print "abc*":
app.get('/abc*', function(req, res) {
res.send('abc*');
});
app.get('/abcd', function(req, res) {
res.send('abcd');
});
The first route handler that matches the route is the one that gets called. That's how Express works in all recent versions. You should generally specify your routes from more specific to less specific and then the more specific will match first and the less specific will catch the rest.
If you want a handler to see all matches and then pass things on to other handlers, you would generally use middleware:
// middleware
app.use("/abc*", function(req, res, next) {
// process request here and either send response or call next()
// to continue processing with other handlers that also match
next();
});
I am using Express 4 with the new router. At least one thing continues to confuse me, and it is a syntax problem - I am wondering if there is a regex that can do what I want. I have a standard REST api, but I want to add batch updates, so that I can send all the info to update some users models with one request, instead of one PUT request per user, for example. Anyway, I currently route all requests to the users resources, like so:
app.use('/users, userRoutes);
in userRoutes.js:
router.get('/', function (req, res, next) {
//gets all users
});
router.put('/:user_id', function (req, res, next) {
//updates a single user
});
but now I want a route that captures a batch request, something like this:
router.put('/Batch', function (req, res, next) {
//this picks up an array of users from the JSON in req.body and updates all
});
in other words, I want something which translates to:
app.use('/usersBatch, function(req,res,next){
}
...but with the new router. I can't get the syntax right.
I tried this:
app.use('/users*, userRoutes);
but that doesn't work. Does anyone know how to design this?
I'm guessing that the call to [PUT] /users/Batch is being picked up by the [PUT] /users/:user_id route. The string /:user_id is used as a regular expression causing it to also collect /Batch.
You can either move /Batch before /:user_id in the route order, refine the regex of /:user_id to not catch /Batch or change /Batch to something that won't get picked up too early.
(plus all the stuff Michael said)
REST doesn't include a POST as a list syntax. That's because each URL in REST point to an individual resource.
As an internet engineer I haven't seen any bulk PUTs or POSTs, but that said, it's your app, so you can make whatever API you like. There are definitely use cases for it.
You'll still need to describe it to Express. I would do it like this:
var express = require('express');
var router = express.Router();
router.get('/', function (req, res) {}); // gets all users
router.post('/:user_id', function (req, res) {}); // one user
router.put('/:user_id', function (req, res) {}); // one user
router.patch('/:user_id', function (req, res) {}); // one user
router.delete('/:user_id', function (req, res) {}); // one user
app.use('/user', router); // Notice the /user/:user_id is *singular*
var pluralRouter = express.Router();
pluralRouter.post('/', function (req, res) {
// req.body is an array. Process this array with a foreach
// using more or less the same code you used in router.post()
});
pluralRouter.put('/', function (req, res) {
// req.body is another array. Have items in the array include
// their unique ID's. Process this array with a foreach
// using +/- the same code in router.put()
});
app.use('/users', pluralRouter); // Notice the PUT /users/ is *plural*
There are other ways to do this. Including putting comma-delimited parameters in the URL. E.g.
GET /user/1,2,3,4
But this isn't that awesome to use, and vague in a PUT or POST. Parallel arrays are evil.
All in all, it's a niche use case, so do what works best. Remember, the server is built to serve the client. Figure out what the client needs and serve.
Is there a useful difference between app.all("*", … ) and app.use("/", … ) in Express.js running on Node.js?
In most cases they would work equivalently. The biggest difference is the order in which middleware would be applied:
app.all() attaches to the application's router, so it's used whenever the app.router middleware is reached (which handles all the method routes... GET, POST, etc).
NOTICE: app.router has been deprecated in express 4.x
app.use() attaches to the application's main middleware stack, so it's used in the order specified by middleware, e.g., if you put it first, it will be the first thing to run. If you put it last, (after the router), it usually won't be run at all.
Usually, if you want to do something globally to all routes, app.use() is the better option. Also, it has less chance of future bugs, since express 0.4 will probably drop the implicit router (meaning, the position of the router in middleware will be more important than it is right now, since you technically don't even have to use it right now).
app.use takes only one callback function and it's meant for Middleware. Middleware usually doesn't handle request and response, (technically they can) they just process input data, and hand over it to next handler in queue.
app.use([path], function)
app.all takes multiple callbacks, and meant for routing. with multiple callbacks you can filter requests and send responses. Its explained in Filters on express.js
app.all(path, [callback...], callback)
app.use only sees whether url starts with the specified path
app.use( "/product" , mymiddleware);
// will match /product
// will match /product/cool
// will match /product/foo
app.all will match complete path
app.all( "/product" , handler);
// will match /product
// won't match /product/cool <-- important
// won't match /product/foo <-- important
app.all( "/product/*" , handler);
// won't match /product <-- Important
// will match /product/
// will match /product/cool
// will match /product/foo
app.use:
inject middlware to your front controller configuring for instance: header, cookies, sessions, etc.
must be written before app[http_method] otherwise there will be not executed.
several calls are processed in the order of writing
app.all:
(like app[http_method]) is used for configuring routes' controllers
"all" means it applies on all http methods.
several calls are processed in the order of writing
Look at this expressJs code sample:
var express = require('express');
var app = express();
app.use(function frontControllerMiddlewareExecuted(req, res, next){
console.log('(1) this frontControllerMiddlewareExecuted is executed');
next();
});
app.all('*', function(req, res, next){
console.log('(2) route middleware for all method and path pattern "*", executed first and can do stuff before going next');
next();
});
app.all('/hello', function(req, res, next){
console.log('(3) route middleware for all method and path pattern "/hello", executed second and can do stuff before going next');
next();
});
app.use(function frontControllerMiddlewareNotExecuted(req, res, next){
console.log('(4) this frontControllerMiddlewareNotExecuted is not executed');
next();
});
app.get('/hello', function(req, res){
console.log('(5) route middleware for method GET and path patter "/hello", executed last and I do my stuff sending response');
res.send('Hello World');
});
app.listen(80);
Here is the log when accessing route '/hello':
(1) this frontControllerMiddlewareExecuted is executed
(2) route middleware for all method and path pattern "*", executed first and can do stuff before going next
(3) route middleware for all method and path pattern "/hello", executed second and can do stuff before going next
(5) route middleware for method GET and path patter "/hello", executed last and I do my stuff sending response
With app.use(), the "mount" path is stripped and is not visible to the middleware function:
app.use('/static', express.static(__dirname + '/public'));
Mounted middleware functions(express.static) are not invoked unless the req.url contains this prefix (/static), at which point it is stripped when the function is invoked.
With app.all(), there is no that behavior.
Yes, app.all() gets called when a particular URI is requested with any type of request method (POST, GET, PUT, or DELETE)
On other hand app.use() is used for any middleware you might have and it mounts onto a path prefix, and will be called anytime a URI under that route is requested.
Here is the documentation for app.all & app.use.
Two differences all above answers don't mention.
The first one:
app.all accepts a regex as its path parameter. app.use does NOT accept a regex.
The second one:
app.all(path, handler) or app[method](path, handler) handlers' path must be same to all path. This is, app[method] path is complete.
app.use(path, handler), if use's path is complete, the handler's path must be /. If the use's path is the start of the complete path, the handler path must be the rest of the complete path.
app.use("/users", users);
//users.js: the handler will be called when matchs `/user/` path
router.get("/", function (req, res, next) {
res.send("respond with a resource");
});
// others.js: the handler will be called when matches `/users/users` path
router.get("/users", function (req, res, next) {
res.send("respond with a resource");
});
app.all("/users", users);
//others.js: the handler will be called when matches `/`path
router.get("/", function (req, res, next) {
res.send("respond with a resource");
});
//users.js: the handler will be called when matches `/users` path
router.get("/users", function (req, res, next) {
res.send("respond with a resource");
});
There are two main differences:
1. pattern matching (answer given by Palani)
2. next(route) won't work inside the function body of middleware loaded using app.use . This is stated in the link from the docs:
NOTE: next('route') will work only in middleware functions that were loaded by using the app.METHOD() or router.METHOD() functions.
Link: http://expressjs.com/en/guide/using-middleware.html
The working effect of next('route') can be seen from the following example:
app.get('/',
(req,res,next)=>{console.log("1");
next(route); //The code here skips ALL the following middlewares
}
(req,res,next)=>{next();}, //skipped
(req,res,next)=>{next();} //skipped
);
//Not skipped
app.get('/',function(req,res,next){console.log("2");next();});