Back to Interview PrepGet interview ready with AILearn each topic the way interviewers ask it, then answer like you would in the interview.
Course progress0/12 chapters · 0%

Chapter 5 of 13

~11 min

Express Router & Modular Routes

As an app grows past a handful of routes, keeping everything in one file becomes unwieldy. express.Router() lets you define a self-contained set of routes in its own module and mount it onto the main app at a chosen path prefix.

What express.Router() Is

express.Router() creates a mini, self-contained instance of Express’s routing system - it supports the same .get()/.post()/.use() methods as the main app object, but is not itself a server. It is meant to be built up in its own file and then attached to a real app (or another router) elsewhere.

Splitting Routes Across Files

A typical pattern is one router module per resource - a usersRouter, a postsRouter - each exporting the router it built via module.exports. This keeps related routes, and any middleware specific to them, grouped together in a single file instead of scattered across one giant server.js.

server.js
// routes/users.js
const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
  res.json({ users: [] });
});

router.get('/:id', (req, res) => {
  res.json({ id: req.params.id });
});

router.post('/', (req, res) => {
  res.status(201).json({ message: 'User created' });
});

module.exports = router;
A router module for user-related routes

Mounting a Router with app.use()

A router is attached to the app using app.use(path, router), where path becomes a prefix stripped from the front of every route defined inside that router. This means the router itself only ever needs to know about routes relative to its own mount point, and the same router module can even be reused at a different prefix if needed.

server.js
// app.js
const express = require('express');
const usersRouter = require('./routes/users');

const app = express();

app.use('/users', usersRouter);

app.listen(3000);

// GET  /users     -> matches router.get('/')
// GET  /users/42  -> matches router.get('/:id')
// POST /users     -> matches router.post('/')
Mounting the users router

Router-Level Middleware

Because a Router supports .use() just like the app object does, middleware can be scoped to only the routes inside that router - for example, requiring authentication for every route under /admin without affecting routes mounted elsewhere. Router-level middleware runs after any app-level middleware registered before the router was mounted, following the same top-to-bottom ordering rule as the rest of Express.

server.js
// routes/admin.js
const express = require('express');
const router = express.Router();

router.use((req, res, next) => {
  if (!req.headers['x-admin-token']) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  next();
});

router.get('/stats', (req, res) => {
  res.json({ visitors: 1234 });
});

module.exports = router;

Coding challenge

Create a router module for a "products" resource with GET / (list) and GET /:id (single product) routes, then mount it in the main app under the /products path prefix.

// Write your solution here

Answer every question below to mark this chapter as complete.

Quiz Yourself

Score: 0/4

1. What is express.Router() best described as?

2. When you write app.use('/users', usersRouter), and the router defines router.get('/:id', ...), which URL matches that route?

3. What is the main benefit of splitting routes into per-resource router modules?

4. How does router.use() (middleware registered directly on a Router) differ in scope from app.use() on the main app?

You're viewing a free lesson. Sign up free to track your progress and unlock the full course.Sign up freeSign in