Express routers don't keep req params - node.js

When you define an endpoint with app.use('/users/:id', router), and then define a route in router like so:
router.get('/randomroute', function(req, res, next) {
console.log(req.params.id); // undefined
});
req.params.id is undefined.
:id is present in the url handled by this route, and you still make one HTTP request, so that doesn't make much sense to me.

Below code is a correct way to do that
app.use('/users', router)
router.get('/:id/randomroute', function(req, res, next) {
console.log(req.params.id);
});

Related

How do I POST/GET to the root of a route?

In my very simple app I have a users route which is hit when I browse to http://localhost/api/users
Is it possible for me to handle a post or get request to that url without appending anything extra to the route? Using the code below the route handler fires when I post to http://localhost/api/users/new but not to http://localhost/api/users and when I try get http://localhost/api/users/13 but not http://localhost/api/users
I know I could use router.post('/', function(req, res) {}); to post to http://localhost/api/users/ but that extra slash seems inelegant
app.js
var express = require('express');
var users = require('./routes/user');
var app = express();
app.use('/api/users', users);
module.exports = app;
routes\user.js
var express = require('express');
var User = require('../models/user');
var router = express.Router();
router.post(function(req, res) {
// post to root
});
router.post('/new', function(req, res) {
// post to /new
});
router.get(function (req, res, next) {
// get root
});
router.get('/:id', function (req, res, next) {
// get /id
});
module.exports = router;
in routes/user.js, you can simply write:
router.post('/', function (req, res, next) {
// post to /api/user or /api/user/
});
router.get('/', function (req, res, next) {
// get /api/user or /api/user/
});
This would work for both: http://localhost/api/users as well as http://localhost/api/users/
Also, there's nothing inelegant about having a / at the end of the url!
You can use an empty route like this :
router.get("", function (req, res, next) {
// get root
});
You will be able to access to /api/user as well as /api/user/
Also you can handle the route with the route method of router to simplify the code and make it more "elegant" :
router.route('/')
.get(function(req, res){}) // GET method of /api/users
.post(function(req, res){}) // POST method of /api/users
.put(function(req, res){}) // PUT method of /api/users
.delete(function(req, res){}) // DELETE method of /api/users
http://expressjs.com/es/api.html#router.route

how can I be more specific with express router params

I currently have a few router routes
router.route('/invite/token/:inviteToken')
.get(function (req, res) {
res.status(200).json(req.invite);
});
router.route('/invite/:inviteId')
.get(function (req, res) {
res.status(200).json(req.invite);
});
And the following simple router params:
router.param('inviteToken', function (req, res, next, inviteToken) {
console.log('inviteToken');
// populate req.invite
next();
});
router.param('inviteId', function (req, res, next, inviteId) {
console.log('inviteId');
// populate req.invite
next();
});
However when I try to fetch an invite by token the inviteId param handler is always triggered first with the literal value "token". Is there an issue with the way I've set up the routes and params?
Update 1 For more clarification
Route order definition matters, so the best practice is to go from most specific to most grabby.
app.get('/invite/token/:token', tokenHandler);
app.get('/invite/:inviteId', inviteHandler);
where tokenHandler and inviteHandler are appropriately formatted callback functions.

Express - public directory divided for authorized/unauthorized users

I have an app written in express.js and I'm trying to divide this application to 2 sections:
one for unauthorized users (with routes only to / - landing page, /login and /* - error404)
and second (routes will be: / - landing page, /app/* - angular SPA which will handle routing on its own)
Express is also configured to take static files from /unauth/public/
And I want to add second static folder for request from authorized routes - /auth/public
which goes to /app/*
My route config looks like this:
var authRoutes = express.Router();
var unauthRoutes = express.Router();
authRoutes.get('/app/*', function(req, res, next) {
if(!req.isAuthenticated())
return res.redirect("/login/");
res.send("AUTHORIZED");
});
unauthRoutes.get('/', function(req, res, next) {
res.send("LANDING PAGE");
});
unauthRoutes.get('/login/', function(req, res, next) {
if(req.isAuthenticated())
return res.redirect("/app/");
res.send("LOGIN PAGE");
});
unauthRoutes.get('/registration/', function(req, res, next) {
if(req.isAuthenticated())
return res.redirect("/app/");
res.send("REGISTRATION PAGE");
});
unauthRoutes.get('/*', function(req, res, next) {
res.send("ERROR 404");
});
app.use('/', authRoutes);
app.use('/', unauthRoutes);
I tried to modify req.url and call another static oruter express.static('auth/public') based on this:
Using express.static middleware in an authorized route
But I don't know, how to handle route app.get('/auth/*', ...) - previous modification will replace url and this route will never be called..
You could try something like this:
// Create your static middlewares
var unauthStatic = express.static('unauth/public');
var authStatic = express.static('auth/public');
// This goes in place of where you would normally load your static middleware
app.use(function(req, res, next) {
if (req.isAuthenticated()) {
authStatic(req, res, next);
} else {
unauthStatic(req, res, next);
}
});
edit:
if you want authenticated users to be able to access files from both the auth and unauth directories, you can make two calls to app.use, like this:
app.use(unauthStatic);
app.use(function(req, res, next) {
if (! req.isAuthenticated()) {
return next();
}
authStatic(req, res, next);
});
Remember that express uses middleware in a stack, meaning to serve a given request, all registered middleware is used in the order it's used. Once a bit of middleware calls req.send, no further middleware gets executed. Anyway, try something like this:
function Authorization(req, res, next) {
if(!req.isAuthenticated())
return res.redirect("/login");
next();
}
var AnonRouter = express.Router()
// GET /style.css will request /unauth/public/style.css
.use(express.static('unauth/public'))
.get('/', function (req, res) { })
.get('/login', function (req, res) { });
var AuthRouter = express.Router()
.use(Authorization)
// GET /app/style.css will request /auth/public/style.css
.use(express.static('auth/public'))
.get('*', function (req, res, next) {
// Handle reqs for non-static files
});
app.use('/', AnonRouter);
app.use('/app', AuthRouter);
app.get('*', function (req, res) {
res.status(404).send('404!');
});
But I don't know, how to handle route app.get('/auth/*', ...) - previous modification will replace url and this route will never be called..
This statement makes me think that you are trying to somehow handle the request after express's static middleware has been called. This is not possible: the static middleware serves static files and you cannot execute additional logic after it does so, but you can run stuff before! Note that in my code, the Authorization middleware will run before the static file is sent.

404 error when Routing using Express and Nodejs

I have created a basic Node/Express App and am trying to implement routes based on separation of logic in different files.
In Server.js
var app = express();
var router = express.Router();
require('./app/routes/users')(router);
require('./app/routes/events')(router);
require('./app/routes/subscribe')(router);
require('./app/routes/login')(router);
app.use('/api',router);
In ./app/routes/users.js
module.exports = function(router){
router.route('/users/')
.all(function(req, res, next) {
// runs for all HTTP verbs first
// think of it as route specific middleware!
})
.get(function(req, res, next) {
res.json(req.user);
})
.put(function(req, res, next) {
// just an example of maybe updating the user
req.user.name = req.params.name;
// save user ... etc
res.json(req.user);
})
.post(function(req, res, next) {
next(new Error('not implemented'));
})
.delete(function(req, res, next) {
next(new Error('not implemented'));
})
router.route('/users/:user_id')
.all(function(req, res, next) {
// runs for all HTTP verbs first
// think of it as route specific middleware!
})
.get(function(req, res, next) {
res.json(req.user);
})
.put(function(req, res, next) {
// just an example of maybe updating the user
req.user.name = req.params.name;
// save user ... etc
res.json(req.user);
})
.post(function(req, res, next) {
next(new Error('not implemented'));
})
.delete(function(req, res, next) {
next(new Error('not implemented'));
})
}
All of the routes are returning 404-Not Found.
Does anyone have suggestions on the best way to implement modular routing in Express Apps ?
Is it possible to load multiple routes in a single Instance of express.Router() ?
------------Edit---------------
On Further Testing
I've been able to debug the express.Router() local instance, and the routing layer stack in the local "router" variable is being updated with the routes from the individual modules.
The last line:
app.use('/api', router);
is also successfully updating the global app instance internal app.router object with the correct routing layers from the local router instance passed to it.
I think the issue is that the Routes for the '/api' are at number 13-14 in the routing layer stack so there is some issue further up the stack with some other middleware routing not letting the routes through to the end... I just need to track this down I guess.
Two issues here :
(1) Looks like the router.route().all was not returning a result, or calling the next() route in the layer.
There is an article here also.
https://groups.google.com/forum/#!topic/express-js/zk_KCgCFxLc
If I remove the .all or insert next() into the .all function, the routing works correctly.
(2) the trailing'/' in the route definition was causing another error
i.e. router.route('/users/') should be router.route('/users')
The slash is important.
Try the following way,
Server.js
app.use('/users' , require('app/routes/users'));
app.use('/events' , require('app/routes/events'));
app.use('/subscribe' , require('app/routes/subscribe'));
In you app/routes/users.js
var router = express.Router();
router.get('/', function (req, res, next) {
//code here
})
router.get('/:id', function (req, res, next) {
//code here
})
module.exports = router;

Adding middleware after app.router

I have a server with the following defined :
app.get('/', function(req, res) {
// gets something
}
app.post('/', function(req, res) {
// updates something, need to be authenticated
}
Now I want the post action to be only for authenticated users, so I want to add a auth middleware between them like this :
app.get('/', function(req, res) {
// gets something
}
app.use('/', function(req, res) {
// check for authentication
}
app.post('/', function(req, res) {
// updates something, need to be authenticated
}
This way, GET gets through and for POST, user has to be authenticated.
The problem is that express doesn't go in to my app.use middleware. If i put the app.use middleware before all of the app.VERB routes, it works.
Is there any way to do it like I want ?
When you declare your first route, Express automatically inserts app.router into the middleware chain. Since the router gets to handle any following routes, any middleware that you declare after that first route will not get to handle your routes.
But instead of using app.use, you can use the fact that route-handlers are very similar to middleware:
app.get('/', function(req, res) {
// gets something
});
app.all('/', function(req, res, next) { // catches GET, POST, ... to '/'
// check for authentication
});
app.post('/', function(req, res) {
// updates something, need to be authenticated
});
But if you only have a single route that needs to be passed through the middleware, it makes sense to follow the advise of #hiattp and add the middleware to the route declaration immediately.
I like to put this type of check in a reuseable method and pass it into the route handler:
function ensureAuth(req, res, next){
if(req.user) next(); // Auth check
else res.redirect('/');
}
app.post('/', ensureAuth, function(req,res){
// User is authenticated
}

Resources