Rest with Express.js nested router
You can nest routers by attaching them as middleware on an other router, with or without params
.
You must pass {mergeParams: true}
to the child router if you want to access the params
from the parent router.
mergeParams
was introduced in Express 4.5.0
(Jul 5 2014)
In this example the itemRouter
gets attached to the userRouter
on the /:userId/items
route
This will result in following possible routes:
GET /user
-> hello user
GET /user/5
-> hello user 5
GET /user/5/items
-> hello items from user 5
GET /user/5/items/6
-> hello item 6 from user 5
var express = require('express');
var app = express();
var userRouter = express.Router();
// you need to set mergeParams: true on the router,
// if you want to access params from the parent router
var itemRouter = express.Router({mergeParams: true});
// you can nest routers by attaching them as middleware:
userRouter.use('/:userId/items', itemRouter);
userRouter.route('/')
.get(function (req, res) {
res.status(200)
.send('hello users');
});
userRouter.route('/:userId')
.get(function (req, res) {
res.status(200)
.send('hello user ' + req.params.userId);
});
itemRouter.route('/')
.get(function (req, res) {
res.status(200)
.send('hello items from user ' + req.params.userId);
});
itemRouter.route('/:itemId')
.get(function (req, res) {
res.status(200)
.send('hello item ' + req.params.itemId + ' from user ' + req.params.userId);
});
app.use('/user', userRouter);
app.listen(3003);
var userRouter = require('express').Router();
var itemRouter = require('express').Router({ mergeParams: true });
userRouter.route('/')
.get(function(req, res) {})
.post(function(req, res) {})
userRouter.route('/:user_id')
.get(function() {})
itemRouter.route('/')
.get(function(req, res) {})
.post(function(req, res) {})
itemRouter.route('/:item_id')
.get(function(req, res) {
return res.send(req.params);
});
app.use('/user/', userRouter);
app.use('/user/:user_id/item', itemRouter);
The key to the second part of your question is the use of the mergeParams option
var itemRouter = require('express').Router({ mergeParams: true });
From /user/jordan/item/cat
I get a reponse:
{"user_id":"jordan","item_id":"cat"}
manageable nested routes...
I wanted a specific example of doing nested routes in a very manageable way in express 4 and this was the top search result for "nested routes in express". Here's an API that would have many routes that would need to be broken up for example.
./index.js:
var app = require('express')();
// anything beginning with "/api" will go into this
app.use('/api', require('./routes/api'));
app.listen(3000);
./routes/api/index.js:
var router = require('express').Router();
// split up route handling
router.use('/products', require('./products'));
router.use('/categories', require('./categories'));
// etc.
module.exports = router;
./routes/api/products.js:
var router = require('express').Router();
// api/products
router.get('/', function(req, res) {
res.json({ products: [] });
});
// api/products/:id
router.get('/:id', function(req, res) {
res.json({ id: req.params.id });
});
module.exports = router;
Nesting example in folder structure
I noticed some comments on "nesting folder structure". It is implied in this however not obvious so I added the section below. Here's a specific example of a nested folder structure for routes.
index.js
/api
index.js
/admin
index.js
/users
index.js
list.js
/permissions
index.js
list.js
This is more a general example of how node works. If you use "index.js" in folders similarly to how "index.html" works in web pages for a directory default, this will be easy to scale your organization based off of recursion without changing your entry points to code. "index.js" is the default document accessed when using require in a directory.
contents of index.js
const express = require('express');
const router = express.Router();
router.use('/api', require('./api'));
module.exports = router;
contents of /api/index.js
const express = require('express');
const router = express.Router();
router.use('/admin', require('./admin'));
module.exports = router;
contents of /api/admin/index.js
const express = require('express');
const router = express.Router();
router.use('/users', require('./users'));
router.use('/permissions', require('./permissions'));
module.exports = router;
contents of /api/admin/users/index.js
const express = require('express');
const router = express.Router();
router.get('/', require('./list'));
module.exports = router;
There is some DRY issues here possibly but it does lend itself well to encapsulation of concerns.
FYI, recently I got into actionhero and have found it to be full featured w/sockets and tasks, more like a true framework all-in-one flipping the REST paradigm on its head. You should probably check it out over going naked w/ express.