Skip to content

04 Routing

DodaTech 3 min read

title: Routing in Node.js REST APIs — Complete Guide weight: 14 date: 2026-06-28 lastmod: 2026-06-28 description: Master Express Router for REST APIs including route parameters, query strings, modular routing, route grouping, and nested routers for clean endpoint organization. tags: [api-development, nodejs]


Express Router enables modular route definitions for REST APIs, with support for route parameters, query string parsing, router-level middleware, and nested router mounting for clean endpoint organization.

```mermaid
flowchart TD
  A[Express Router] --> B[Router Instance]
  B --> C[router.get('/users', handler)]
  B --> D[router.post('/users', handler)]
  B --> E[router.param('id', middleware)]
  A --> F[Nested Routers]
  F --> G[app.use('/api/users', userRouter)]
  F --> H[app.use('/api/orders', orderRouter)]
  style A fill:#e1f5fe
  style B fill:#c8e6c9

The Express Router is a mini Express application that can contain middleware and routes. You create a router instance, define routes on it, and mount it on the main app at a base path. This keeps route definitions close to their related logic.

Think of Express Router like a department store directory. Each floor (router) has its own departments (routes). The main entrance (app) directs you to the right floor based on what you need.

Example: Creating and Mounting Routers

// src/routes/userRoutes.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');

// GET /api/users
router.get('/', userController.listUsers);

// GET /api/users/:id
router.get('/:id', userController.getUser);

// POST /api/users
router.post('/', userController.createUser);

module.exports = router;

// src/app.js
const userRoutes = require('./routes/userRoutes');
app.use('/api/users', userRoutes);

Example: Route Parameters and Query Strings

const express = require('express');
const router = express.Router();

// Route parameter
router.get('/users/:userId/orders/:orderId', (req, res) => {
  const { userId, orderId } = req.params;
  res.json({
    message: `Fetching order ${orderId} for user ${userId}`
  });
});

// Query string
router.get('/products', (req, res) => {
  const { category, minPrice, maxPrice, sort } = req.query;
  res.json({
    filters: { category, minPrice, maxPrice },
    sort,
    message: 'Filtered products'
  });
});

// Test: GET /products?category=electronics&minPrice=100&sort=-price

Expected output:

{"filters": {"category": "electronics", "minPrice": "100"}, "sort": "-price", "message": "Filtered products"}

Example: Router-Level Middleware

const express = require('express');
const router = express.Router();

// Middleware runs for all routes in this router
router.use((req, res, next) => {
  console.log(`[User API] ${req.method} ${req.originalUrl}`);
  next();
});

// Route-specific middleware
const validateUserId = (req, res, next) => {
  const id = parseInt(req.params.id);
  if (isNaN(id) || id <= 0) {
    return res.status(400).json({ error: 'Invalid user ID' });
  }
  req.userId = id;
  next();
};

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

module.exports = router;

Common Mistakes

  1. Route order sensitivity — Express matches routes in order. Place parameterized routes after static routes: /users/new before /users/:id.
  2. Missing route for 404s — Add a catch-all route at the end of your router to handle undefined endpoints with a proper 404 response.
  3. Overlapping route patterns/users/:id and /users/profile conflict when profile is treated as an ID. Use specific paths or different prefixes.
  4. Not using router.param() for parameter validation — router.param('id', validator) runs middleware automatically when :id is present in the route.
  5. Mixing route definitions and business logic — Route handlers should be one-liners calling controller methods, not containing business logic.

Practice Questions

  1. How do you create a nested route for orders under users?
  2. What is the difference between req.params and req.query?
  3. Why does route order matter in Express?
  4. How do you add middleware to a specific route?
  5. Challenge: Set up routing for a library API with nested routers for books, authors, and members. Include a route parameter for book ID and query parameters for filtering books by genre and year.

FAQ

What is the difference between app.get and router.get?

app.get mounts a route directly on the application. router.get mounts on a router that can be mounted on the app with a prefix. Routers are modular and reusable.

How do I handle 404 routes?

Add a catch-all middleware at the end of your route definitions: app.use('*', (req, res) => res.status(404).json({error: 'Not found'})).

Can I have multiple route parameters?

Yes: /users/:userId/orders/:orderId. Access them as req.params.userId and req.params.orderId.

What happens when a route is not found?

Express moves to the next matching route. If no route matches, the catch-all 404 handler responds. Without it, Express returns a default HTML 404.

How do I make route parameters optional?

Use a question mark: /users/:id?. Access req.params.id which will be undefined if not provided. Alternatively, use query parameters.

Mini Project

Build a routing system for a blog API with the following routes: GET /posts, GET /posts/:id, GET /posts/:id/comments, POST /posts, POST /posts/:id/comments, GET /users/:userId/posts. Include query parameters for pagination and filtering on list endpoints.

What's Next

Now learn about middleware basics in Building REST APIs with Node.js.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro