express routing and path params - node.js

I have the following routing in app.js
app.use("/api/:phone/stuff", userRoutes);
In userRoutes, I have the following
userRoutes.get("/", userController.getStuff);
The client (Android app, retrofit) requests the following url: GET /api/+155555555/stuff, in userService I have getStuff method which is executed as expected, meaning it routes it well.
in a validation middleware, I see that req.originalUrl contains the url as expected but when viewing req.params, it is an empty object. I was expecting it to link the phone part of the url to :phone param so I will have req.params.phone.
I thought the problem was the + sign but it is urlencoded and even without it, it didn't work. What am I missing?

By default express router does not inherit parent router params
const userRoutes = express.Router({ mergeParams: true });
should do the trick
http://expressjs.com/en/4x/api.html#express.router

Related

TypeError: register.route(...).use is not a function

I'm using express.Router() whenever i try use method it gives the following error
TypeError: register.route(...).use is not a function
Code
/server/routes
const express = require('express');
const register = express.Router();
const account = require("../controller/AccountController");
const Middleware = require("../utils/middlewares");
register.route('/')
.post(Middleware.checkUser)
.post(account.user_register)
register.route('/verify/:token')
.get(Middleware.verifyEmail)
register.route('/resend/:email')
.use(Middleware.sendVerification)
module.exports = register;
Server.js
server.use('/register', register);
When i use a method like get there is no error. But i don't want to use any method since the middleware just sends an email
As stated in documentation, route all method is intended for route-specific middleware, it could be:
register.route('/resend/:email')
.all(Middleware.sendVerification)
If the route is expected to be requested with GET only and may not make sense for other verbs, it should be narrowed down to supported verbs:
register.route('/resend/:email')
.get(Middleware.sendVerification)
In this case sendVerification seems to be route handler and not a middleware. It's suitable to specify it only for get if /resend/ is expected to be requested with GET.

ExpressJS Applying middleware only to routes in router

I have app where I have public routes and authorized routes. Public routes should go through auth as well, but if auth fails, it doesn't matter.
So I have two routers:
var publicRoutes = express.Router();
var secretRoutes = express.Router();
publicRoutes
.use(auth)
.use(ignoreAuthError);
publicRoutes.get('/public', function(req, res){
res.status(200).send({message: "public"});
});
secretRoutes
.use(auth)
.use(handleAuthError);
secretRoutes.get('/secret', function(req, res){
res.status(200).send({message: "secret"});
});
...
app.use(publicRoutes);
app.use(secretRoutes);
Now everything works fine, but if I change the order of app.use public routes throw auth error. Also I cannot get any 404, 500 etc errors, because they all go through auth errors.
So obviously what is happening is that Router.use() is being applied to all routes with the same root - in this case "/"
Therefore I think if I would use just auth middleware on all routes and then add other middlewares directly to routes it should work fine. But it kind of brakes the point of having multiple Routers for me.
I would expect that if I use Router.use() the middleware will apply only if that particular router matches any routes it has set up, instead of changing behavior of other router.
Do I understand this correctly? Is there any way to handle this without actually having to add middleware to every single route?
Had the same issue, solved thanks to #Explosion Pills comment.
Bad:
app.use(secretRoutes); // router.use calls won't be scoped to "/secret"
app.use(publicRoutes); // public routes will be impacted
Good:
app.use("/secret", secretRoutes); // router.use calls will be scoped to "/secret"
app.use("/public", publicRoutes); // public routes won't be impacted

Express 4 router.param doesnt fire

I have a node express 4 app and I want to mount a route (contacts) on a parent route. i.e
/:fundid/contacts
In my fund route I declare
var router = require('express').Router({ mergeParams: true });
var contactRoutes = require('./contacts');
router.use('/:fundid/contacts', contactRoutes);
In my contact route
var router = require('express').Router({ mergeParams: true });
router.param('fundid', function(res, req, next, id){});
The problem is that this param call does not fire. From what I can garner from the documentation these param calls are relative to the router they are declared on, but i would have thought mergeParams:true would affect this, but it doesn't. The route is otherwise working, and both routes are called. Am I missing something?
The reason I want to do this is because I want to mount the contacts route on multiple parent routes, and build a filter based on those parent parameters
This comment suggests that parameters are tied to the router they are declared with; so in your case, fundid can only be handled by the "fund" router. mergeParams serves a different purpose, namely to provide access to req.params.fundid from child routers.
You can always use a request middleware in your contact router to perform special operations based on the fundid, though:
router.use(function(req, res, next) {
var id = req.params.fundid;
...
});

Express adds slash at the end of url weirdly

I'm using Node.js and Express framework for developing a website. I faced weird misbehaviour with a url. When i click to related link url, url becomes "localhost:3000/images/" - a slash is added at the end as you can see. But when i change all 'images' to 'img' or else url becomes "localhost:3000/img" no slash added. Why router behaves like that? Codes written below. (I'm using Jade template Engine)
//bar.jade
li.nav-item
a.nav-link(href='images')
i.icon-camera
| Images
//end of bar.jade
//images.js (router)
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('images', { title: 'Express'});
});
module.exports = router;
//end of router .js
//app.js
var images =require('./routes/images');
........
........
app.use('/images',images);
//end of app.js
I think I know what's going on: you're also using the express.static() middleware, and in your public directory you have a directory called images/.
This middleware will generate redirects ending with a slash when you try to request a URL that matches a public directory (even when that directory is empty or matches another route).
To disable this behaviour, set the redirect option to false.
By default in express router “/foo” and “/foo/” are treated the same by the router. You can disable this behavior with strict: true option.
Express.Router documentation
var router = express.Router({strict: true});

404 when accessing new route

I'm trying to add a new route (/profile) to my NodeJS Express web application. I've modified my app.js file like this:
var routes = require('./routes/index');
var profile = require('./routes/profile');
app.use('/', routes);
app.use('/profile', profile);
The '/' index path works fine, my issue is with '/profile'. Whenever I try to access it, I get a 404. This is profile.js:
var express = require('express');
var router = express.Router();
router.get('/profile', function(req, res) {
var username = req.session.username;
if(username) {
res.render('profile');
} else {
res.redirect('/login');
}
});
module.exports = router;
I don't understand what I'm doing wrong because in the example express application that is generated, '/users' works fine. I basically copied that format, but it's throwing a 404. Any ideas?
In my profile.js, I had to change my GET request path to this:
router.get('/', function(req, res) {
//code
});
Otherwise, the router would be looking for /profile/profile. When I change it to /, it's just looking for the root of `/profile', or at least that's how I understand it.
To understand what you are doing wrong you should know that Node.js uses middleware functions to route your requests. To simplify you can think about it as a chain of functions.
Middleware is like a plumbing pipe, requests start at the first middleware you define and work their way “down” the middleware stack processing for each path they match.
So with the following statement you added a middleware function to handle any request starting with the root path /profile, and it is a common pattern in Node to use the use method to define the root paths.
app.use('/profile', profile);
The use method is doing part of the routing in your scenario and the statement above will match any route starting with that path, including /profile/all or /profile/12 or even /profile/go/deeper/inside.
However, you want to narrow down that routing to something more specific, so that is why you pass a router middleware function (profile in your case) to match more specific routes instead of all routes starting with /profile.
The profile middleware function is actually the next step in the chain of functions to execute, and it will start from the root path specified in the use statement, which is the reason why you need to start again with / and not with /profile. If you wanted to match a profile by ID you would do:
router.get('/:id', ...)
Which would be concatenated with the base URL (from the /use statement) and would match a request like /profile/2 or /profile/abc.

Resources