ExpressJS Applying middleware only to routes in router - node.js

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

Related

How can I set the routes correctly?

Here is a sample code:
var x = require('./folder/usefile');
var Auth = passport.authenticate('jwt', { session: false });
module.exports = function(app){
console.log('inside function(app)'); //line 1 executed!
var player = express.Router();
var finalRun = express.Router();
app.use('/api/usefile',player);
player.get('/', Auth, x.login);
player.post('/post', Auth, function(req, res){
x.register});
app.use('/api',finalRun);
console.log('inside api'); //line 2 is executed!
}
In usefile my login function is present which is exported.
I am using passport here, whose functionality is present in separate file here it is used only for authentication.
When I ran http://localhost:8080/api/usefile/---> for get method, it is displaying 404. same for post method.
The console is printing line 1 and line 2(have mentioned in the comments) and the flow is line1, line2.
Can anyone help me to find what mistake I have made here?
You've created two Router instances player and finalRun. There are two main issues:
This line player.use('/usefile',player) should be app.use('/usefile',player). express.Router().use is used to add a middleware for all routes registered to the router (in this case player). For example, if you wanted to use the Auth middleware from Passport for every player route, you could declare it as player.use(Auth).
You are not declaring any route handling for the finalRun router. So in this case, your route handlers registered with player will handle host/usefile and finalRun will handle any host/api.
If you are wanting the handlers registered with player to handle /api/usefile, using a single router instance would be far simpler.
Register the /api/usefile path with the player router like:
app.use('/api/usefile', player);
Docs for app.use
Docs for router.use

How to use single piece of middleware with more than one express router?

I am working on a university project and we have decided to go for MEAN technology stack. To be honest I am a beginner with NodeJS and express, more precisely this is the first time I do sth with it.
I've found that is preferable to use express.Router rather than putting all routes to express instance e.g. app.post('path', function(req, res) { ... })
So this is what I have
var express = require('express');
var app = express();
function authorizationMiddleware(req, res, next) {
...
}
// handles login, doesn't meed autorizationMiddleware
var authRouter = express.Router();
authRouter.route('/login')
.post(function (req, res) {
...
});
// handles fetching of a single, all person(s), fetching of transactions for a person
var personRouter = require('./routes/personRoutes')(Person, Transaction, autorizationMiddleware);
//handles adding of a new transaction e.g. POST /api/transactions where params such as sender, recipient and amount are passed in body
var transactionRouther = require('./routes/transactionRoutes')(Person, Transaction, autorizationMiddleware);
app.use('/api', authRouter);
app.use('/api/persons', personRouter);
app.use('/api/transactions', transactionRoutes);
app.listen(8080, function () {
console.log('Listening on port: ' + 8080);
});
As you can see I have three routers (not even sure if I have gonne too far with them), authRouter is handling login only and I have also decided to separate persons logic from transactions logic too. (maybe I could have handled creation of new transaction in a way like /api/persons/:personId/transactions but I rather liked the idea of sending all required params in body).
I would like to ask if you agree with the solution I tried. As you can see I am passing authrizationMiddleware function (handles verification of JWT token) function to router modules and using it there.
Is there maybe a better way to use the same middleware with of multiple routers or is this a legit way?
Thx in advance
Cheers!
I don't get why you use 3 Routers. The "common" way to go (or at least the way I go) is to put all the routes in the same place, except when the path is very different or the purpose is different (for example I separate the error routes from the others).
For example, let's say I need to build a rest api for an app, I would probably have paths like:
/users/:userid
/users/:userid/comments/:commentid
/locations
...
All these routes can go in the same Router and if you want, you can apply specific authentication/authorization middlewares to them:
router.get("/users/:userid",
doAuthentication, authorizeOnUserId,
userController.getUserById);
router.get("/locations",
doAuthentication, authorizeLocations,
locationController.getAllLocations);
The middlewares are called in sequence and the request is passed on to the next middleware only if there are no errors (unauthenticaed/ unhauthorized).
Then you can simply import your routes like this:
app.use('/api', router);
Using this technique allows you to have a fine grain control over your routes.
Hope this helps.

How Express recorgonizes middlewares?

I'm novice in Express and a little bit confused about how it handles middlewares? So basically I have two middlewares which looks like:
app.use(require('_/app/middlewares/errors/404'))
app.use(require('_/app/middlewares/errors/500'))
404
var log = require('_/log')
module.exports = function (req, res, next) {
log.warn('page not found', req.url)
res.status(404).render('errors/404')
}
500
var log = require('_/log')
module.exports = function (er, req, res, next) {
log.error(er.message)
res.locals.error = er
res.status(500).render('errors/500')
}
So now I want to add my custom middleware app.use(require('_/app/middleware/shareLocals')) which looks like:
module.exports = function (req, res, next) {
res.locals.base_url = req.protocol + '://' + req.get('host');
next();
}
The main problem is that now when I try to use base_url I get 404 error...
So how Express understands what middleware do? That is between my middleware and 404 are no visual differences:
it receives same params
it doesn’t have any if's in it, just throws 404 error
Appears the feeling the middlewares in Express are made for errors (when excepts err as first param) and for 404 (when there is no first err)...
P.S.
Is there any difference defining middlewares before or after routes?
P.S. Is there any difference defining middlewares before or after routes?
Yes.
The order in which you register your middlewares (and routes) have a lot to say.
Image express as a giant list. Starting at the first element in the list, you have the first middleware OR route you have defined, next is the second, etc.
When express gets a request, it appears to be matching your route/name of route/middleware, and if it's a hit, it executes the middleware/route and potentially waits for a "next()" call.
So if you have a route "/test" it will only be executed if you have a request matching "/test". routes with different names obviously wont get triggered. middlewares can also have names: app.use("/test", middlewareA). This will also only trigger if "/test" is requested. The way you do it, all requests (within the routes namespace) will be triggered app.use(middlewareA). It's like a wildcard.
Now, to the implications of things being ordered:
Your 404 middleware should only be used AFTER all routes have been defined. that way, when the list reached the 404 middleware, no routes have actually been found.
returning/sending result/not calling next() at the end of a middleware will all potentially create problems in your flow. I wont go into details about this, but be aware of it.
I am guessing your own middleware is added after the 404 middleware. That is probably the problem. If not, you should surrender more of your code so we can take a better look. But remember, order is everything :)

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

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