Routes priority in Express: am I doing it wrong? - node.js

I'm a bit confused about the Express priority order for all the routes.
This is what I've done following some stackoverflow answers and other guides on internet.
I have some http requests and this is the order: is it right in your opinion? If not, why? Thanks!
app.get('/', (req, res) => {
...
});
app.get('/:car', (req, res) => {
...
});
app.get('/:car/:eng', (req, res) => {
...
});
app.put('/:car/:feature_id', (req, res) => {
...
});
app.get('/import/colors', (req, res) => {
...
});
app.post('/import/brands', (req, res) => {
...
});
app.post('/import/colors/:car_id', (req, res) => {
...
});

Express just attempts to match your routes in the order they are defined in your code for the type of request it is (.e.g GET, POST, PUT). So, if it's a GET request, Express matches only against the route definitions that include GET. Here's a few examples from the routes you show.
If there's an incoming route of /hello/there, then that will first match app.get('/:car/:eng', ...) because that matches ANY two segment path and it's the first route in order that matches.
If there's an incoming route of /import/colors, then that will also match app.get('/:car/:eng', ...) because that matches ANY two segment path.
If, on the other hand, you change the order of the code to this:
app.get('/import/colors', (req, res) => {
...
});
app.get('/:car/:eng', (req, res) => {
...
});
Then, /import/colors will match the first of these two routes because matching is tried in the order the routes are declared.
FYI, in general, I don't like using routes that have a top level wildcard because you get yourself all sorts of possible conflicts and you may find yourself boxed into a bit of a corner for future URL expansion when you want to add other routes.
Recommendation
So, if you have overlapping route definitions, then define the most specific routes first so they get a chance to match before the more general routes. Most of the time, I prefer a URL design that doesn't have overlapping routes and then you won't have this issue at all.

Related

How to remove or replace callbacks for a specific path

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

node express: is path always optional?

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*.

Order of route precedence express 3 vs 4

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();
});

Make dynamic path in node and express js

I am trying to make route dynamic like /review-{title}-{id}
, but causing error don't know why, Also if user enter the wrong params than how to handle that.
My client requirement is like above, I am not good in node and express please anyone suggested how to make routes like above.
Also if I needed to make route like this /review/:title/:id format than how can I make like that.
I am trying but it redirect me out to the 404 page,
Please find my existing code details inside,
server.js
this is working..
app.get('/review', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client/review.html'));
});
but not this one..
app.get('/review-*-*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client/review.html'));
});
Also not this one working
app.get('/review/*/*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client/review.html'));
});
This is 404 page which call evrytime while accessing dynamic pages
app.get('/*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client/404.html'));
});
Check out the syntax for routes in Express.
In most cases you're better off using route params, e.g.:
app.get('/review/:title/:id', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client/review.html'));
});
Some more flexibility (but more opaque for most developers) would be to match on regex.
I think you can put a * in the middle of words (they give an example like '/abc*def', but I'm not sure how nicely that plays with the other things you're doing, and I don't think you can have multiple *'s in the pattern if you do that.)

Express 4: router syntax

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.

Resources