Facebook Pixel

Express Router Mounting and Nesting Explained

Mounting and nesting routers is how you scale Express structure. Here is how it works.

Express Router Mounting and Nesting Explained

Mounting and nesting routers is how you scale Express structure. Here is how it works.

Mounting

app.use('/path', router) mounts a router under a path. The router's paths are relative to the mount path.

// routes/users.js
router.get('/', listUsers);        // matches /users
router.post('/', createUser);      // matches /users
router.get('/:id', getUser);       // matches /users/:id
router.patch('/:id', updateUser);  // matches /users/:id
router.delete('/:id', deleteUser); // matches /users/:id

// app.js
app.use('/users', require('./routes/users'));

Why Mount

  • Clean separation: each feature has its own router file.
  • Path prefixing: the mount path becomes the prefix for all routes in the router.
  • Per-router middlewares: apply auth or validation to all routes in the router.

Nesting

Mount a router inside another router:

// routes/users.js
const postsRouter = require('./posts');
router.use('/:userId/posts', postsRouter);

Now /users/:userId/posts/... hits postsRouter.

// routes/posts.js (nested)
router.get('/', listUserPosts);   // matches /users/:userId/posts
router.post('/', createUserPost); // matches /users/:userId/posts
router.get('/:postId', getUserPost); // matches /users/:userId/posts/:postId

Limit Nesting to One Level

/users/:userId/posts/:postId/comments/:commentId is too deep. Use /comments/:id with a filter (?post=postId) instead. Deep nesting makes URLs long and rigid.

Per-Router Middlewares

// routes/admin.js
router.use(auth);
router.use(requireRole('admin'));
router.get('/stats', getStats);

Every route in admin.js requires auth and admin.

Nested Middlewares

// routes/users.js
router.use('/:userId/posts', auth, postsRouter);

The auth middleware runs for /users/:userId/posts but not for /users (if it is not added at the top of users.js).

Versioned Mounting

app.use('/api/v1/users', require('./routes/v1/users'));
app.use('/api/v2/users', require('./routes/v2/users'));

Each version has its own router. Bump on breaking changes.

The Takeaway

Mount routers with app.use('/path', router). The router's paths are relative to the mount path. Nest routers for nested resources (/users/:userId/posts), but limit to one level. Apply per-router middlewares with router.use(). Version APIs with /api/v1, /api/v2.

app.use('/path', router). The router's paths are then relative to /path. For example, router.get('/:id') mounted at /users responds to /users/:id. Mount in app.js to keep it thin.

Yes. Mount one router inside another: router.use('/:userId/posts', postsRouter). Now /users/:userId/posts/... hits postsRouter. Limit nesting to one level; for deeper resources, use a separate top-level router with a filter.

router.use(middleware) at the top of the router file applies the middleware to every route in the router. Useful for auth and role checks across a feature without repeating per route.

Deep nesting (e.g., /users/:userId/posts/:postId/comments/:commentId) makes URLs long, rigid, and hard to refactor. Use a separate top-level router with a filter (?post=postId) for deeper resources. Keeps URLs clean.

Mount routers under /api/v1, /api/v2, etc. app.use('/api/v1/users', require('./routes/v1/users')). Each version has its own router. Bump on breaking changes; non-breaking changes (adding fields) do not need a new version.

Ready to master Node.js completely?

Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.