Express
07 / 07

Routing

Express Routing

Routing determines how an application responds to client requests at specific endpoints. Express provides a full-featured routing system that supports route parameters, query strings, route-level middleware, and modular Router instances for organizing large APIs.

Basic Routing & HTTP Methods

// HTTP method handlers
app.get('/api/users', (req, res) => { /* ... */ });
app.post('/api/users', (req, res) => { /* ... */ });
app.put('/api/users/:id', (req, res) => { /* ... */ });
app.patch('/api/users/:id', (req, res) => { /* ... */ });
app.delete('/api/users/:id', (req, res) => { /* ... */ });
app.options('/api/users', (req, res) => { /* ... */ });

// Handle all HTTP methods
app.all('/api/users', (req, res) => {
  res.json({ method: req.method });
});

// Chained route handlers for the same path
app.route('/api/users/:id')
  .get((req, res) => {
    res.json({ id: req.params.id });
  })
  .put((req, res) => {
    res.json({ updated: req.params.id });
  })
  .delete((req, res) => {
    res.status(204).end();
  });

// Multiple handlers per route (middleware chain)
app.get('/api/data', authenticate, authorize('read'), (req, res) => {
  res.json({ data: 'secret' });
});

Route Parameters & Query Strings

// Route parameters - :param in path
app.get('/users/:userId/posts/:postId', (req, res) => {
  const { userId, postId } = req.params;
  // GET /users/42/posts/7 => { userId: '42', postId: '7' }
  res.json({ userId, postId });
});

// Optional parameter
app.get('/users/:userId/profile/:section?', (req, res) => {
  const section = req.params.section || 'overview';
  res.json({ section });
});

// Query strings
app.get('/api/products', (req, res) => {
  const {
    search = '',
    category,
    page = '1',
    limit = '20',
    sortBy = 'name',
    order = 'asc',
  } = req.query;
  // GET /api/products?search=laptop&category=tech&page=2
  const pageNum = parseInt(page, 10);
  const limitNum = parseInt(limit, 10);
  const offset = (pageNum - 1) * limitNum;

  res.json({ search, category, pageNum, limitNum, offset, sortBy, order });
});

// Route parameter middleware (runs before any route with :userId)
app.param('userId', async (req, res, next, id) => {
  try {
    const user = await User.findById(id);
    if (!user) return res.status(404).json({ error: 'User not found' });
    req.user = user;  // Attach to req for route handlers
    next();
  } catch (err) {
    next(err);
  }
});

Express Router - Modular Routes

Use express.Router() to create modular route handlers. Each router is a mini Express application with its own middleware and routing. Mount routers at a path prefix in your main app.

// routes/users.js
const express = require('express');
const router = express.Router();
const { authenticate } = require('../middleware/auth');
const usersController = require('../controllers/usersController');

// Router-level middleware (applies to all routes in this file)
router.use(authenticate);

// Routes (paths are relative to the mount point)
router.get('/', usersController.list);           // GET /api/users
router.post('/', usersController.create);        // POST /api/users
router.get('/:id', usersController.getById);     // GET /api/users/:id
router.put('/:id', usersController.update);      // PUT /api/users/:id
router.delete('/:id', usersController.remove);   // DELETE /api/users/:id

module.exports = router;

// app.js - mount the router
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);

// Nest routers
const v1Router = express.Router();
v1Router.use('/users', usersRouter);
v1Router.use('/products', productsRouter);
app.use('/api/v1', v1Router);

RESTful API Pattern

// controllers/usersController.js - RESTful controller
const User = require('../models/User');

exports.list = async (req, res, next) => {
  try {
    const users = await User.findAll();
    res.json({ data: users, total: users.length });
  } catch (err) { next(err); }
};

exports.create = async (req, res, next) => {
  try {
    const user = await User.create(req.body);
    res.status(201).json({ data: user });
  } catch (err) { next(err); }
};

exports.getById = async (req, res, next) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) return res.status(404).json({ error: 'User not found' });
    res.json({ data: user });
  } catch (err) { next(err); }
};

exports.update = async (req, res, next) => {
  try {
    const user = await User.findByIdAndUpdate(req.params.id, req.body);
    if (!user) return res.status(404).json({ error: 'User not found' });
    res.json({ data: user });
  } catch (err) { next(err); }
};

exports.remove = async (req, res, next) => {
  try {
    await User.findByIdAndDelete(req.params.id);
    res.status(204).end();
  } catch (err) { next(err); }
};

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free