Node.js: Express Routing

Last updated: 2026-08-26

Charlie’s API project initially had only five routes, all written in app.js, which was still fairly clear. Two months later, the value of routes had grown to 30, and the file exceeded 500 lines—it took forever to make a single change to an API endpoint. The team decided to use express.Router() to split the routes into modules—user routes went into users.js, authentication routes into auth.js, and the custom logging middleware was moved to a separate file. After the split, each file had a single responsibility, and maintenance efficiency improved significantly.

You'll learn:


1. express.Router() Modular Routing

As the value of routes increases, putting all routes in app.js can result in bloated files and make collaboration difficult. express.Router() allows you to create independent route instances and then mount them to a specific path prefix in your application.

Feature Routes defined directly in the app Modular routing via the Router module
File Organization Everything in app.js Split into separate files by module
Path Prefix Specify the full path for each route Set a uniform prefix when mounting
Team Collaboration Multiple Users Editing the Same File Independent Maintenance of Each Module
Reusability Low High; can be reused across projects

▶ Example: Creating a Standalone User Routing Module

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

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

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

module.exports = router;
▶ Try it Yourself

▶ Example: Mounting a route module in app.js

JAVASCRIPT
// app.js
const express = require('express');
const app = express();
const userRouter = require('./routes/users');

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

app.listen(3000, () => {
  console.log('Server running on port 3000');
});
▶ Try it Yourself

Visiting /users matches router.get('/'), and visiting /users/42 matches router.get('/:id').



2. Route Parameters, Query Strings, and Request Bodies

Express offers three ways to receive client data, each suited to different scenarios.

Data Source How to Access URL Example Typical Uses
Routing Parameters req.params /users/42 Identifies a specific resource
Query String req.query /users?role=admin Filter, Sort, Search
Request Body req.body POST body Submit form/JSON data

▶ Example: Route parameter :id

JAVASCRIPT
app.get('/products/:id', (req, res) => {
  const productId = req.params.id;
  res.json({ productId });
});
▶ Try it Yourself

▶ Example: Query string req.query

JAVASCRIPT
app.get('/products', (req, res) => {
  const { category, sort, page } = req.query;
  res.json({ category, sort, page: page || 1 });
});
▶ Try it Yourself

Access /products?category=electronics&sort=price&page=2, req.query, and { category: 'electronics', sort: 'price', page: '2' }.

▶ Example: Request body req.body and express.json()

JAVASCRIPT
app.use(express.json());

app.post('/products', (req, res) => {
  const { name, price } = req.body;
  res.status(201).json({ name, price });
});
▶ Try it Yourself

Note: The values of req.query and req.params are both strings and must be manually converted to numbers.



3. Middleware Basics and the next() Mechanism

Middleware is a core concept of Express—every request passes through a chain of middleware, and each middleware function can read the request, modify the response, or pass control to the next middleware function.

100%
graph LR
  A[Request] --> B[morgan Log]
  B --> C[express.json Analysis]
  C --> D[Custom auth Middleware]
  D --> E[Route Handling Functions]
  E --> F[Response]
  D -->|next error| G[Error-handling middleware]

▶ Example: The Simplest Custom Middleware

JAVASCRIPT
function logger(req, res, next) {
  console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
  next();
}

app.use(logger);
▶ Try it Yourself
Rule Description
next() must be called Otherwise, the request will be suspended, and the client will never receive a response
Call order is the execution order Middleware registered first is executed first
app.use() Applies globally app.use('/api', ...) Applies only to the /api path
next('route') Skip the remaining middleware for the current route


4. Integration with Third-Party Middleware

The community provides a wide range of ready-to-use middleware that can be enabled with a single line of code after installation.

Middleware Purpose Installation Command
cors Resolving Cross-Origin Request Issues npm install cors
morgan HTTP Request Logging npm install morgan
helmet Set up a safety response helmet npm install helmet
express-rate-limit Request Rate Limit npm install express-rate-limit
cookie-parser Parse Cookies npm install cookie-parser

▶ Example: Integrating CORS, Morgan, and Helmet

JAVASCRIPT
const cors = require('cors');
const morgan = require('morgan');
const helmet = require('helmet');

app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.use(express.json());
▶ Try it Yourself

Note: It is recommended to place security-related middleware (Helmet) first, followed immediately by logging middleware.



5. Error-Handling Middleware

Express Convention: A function with four parameters (err, req, res, next) is an error-handling middleware. Whenever any middleware calls next(err), it skips the subsequent regular middleware and proceeds directly to the error-handling middleware.

Comparison Item Standard Middleware Error-Handling Middleware
Number of parameters 3 (req, res, next) 4 (err, req, res, next)
Trigger Method Execute sequentially when requests arrive next(err) Trigger
Registration Location Any Location Must be placed last
Can there be more than one? Yes Yes, executed in order

▶ Example: Global Error Handling Middleware

JAVASCRIPT
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: err.message || 'Internal Server Error'
  });
});
▶ Try it Yourself

▶ Example: Triggering an error in a route

JAVASCRIPT
app.get('/admin', (req, res, next) => {
  const err = new Error('Access denied');
  err.status = 403;
  next(err);
});
▶ Try it Yourself

6. Mermaid: Modular Routing Structure of the Router

100%
graph TD
  APP[app.js] --> UR["/users → userRouter"]
  APP --> AR["/auth → authRouter"]
  APP --> MW["middleware/logger.js"]
  UR --> U1["GET / → User List"]
  UR --> U2["GET /:id → User Details"]
  UR --> U3["POST / → Create a User"]
  AR --> A1["POST /login → Log In"]
  AR --> A2["POST /register → Register"]
  MW --> ML["Logging Middleware"]


7. Comprehensive Example: Modular Routing API

In this example, the user routing, authentication routing, and logging middleware are split into separate files and ultimately integrated into app.js.

▶ Example: middleware/logger.js — Custom logging middleware

JAVASCRIPT
function logger(req, res, next) {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`);
  });
  next();
}

module.exports = logger;
▶ Try it Yourself

▶ Example: routes/users.js — User Routes Module

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

let users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

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

router.get('/:id', (req, res, next) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) {
    const err = new Error('User not found');
    err.status = 404;
    return next(err);
  }
  res.json(user);
});

router.post('/', (req, res) => {
  const { name } = req.body;
  if (!name) {
    return res.status(400).json({ error: 'Name is required' });
  }
  const newUser = { id: users.length + 1, name };
  users.push(newUser);
  res.status(201).json(newUser);
});

module.exports = router;
▶ Try it Yourself

▶ Example: routes/auth.js — Authentication Routes Module

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

router.post('/login', (req, res) => {
  const { username, password } = req.body;
  if (!username || !password) {
    return res.status(400).json({ error: 'Username and password required' });
  }
  res.json({ message: 'Login successful', token: 'mock-jwt-token' });
});

router.post('/register', (req, res) => {
  const { username, email, password } = req.body;
  if (!username || !email || !password) {
    return res.status(400).json({ error: 'All fields are required' });
  }
  res.status(201).json({ message: 'Registration successful' });
});

module.exports = router;
▶ Try it Yourself

▶ Example: app.js — Integration Entry Point

JAVASCRIPT
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');

const logger = require('./middleware/logger');
const userRouter = require('./routes/users');
const authRouter = require('./routes/auth');

const app = express();

app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.use(express.json());
app.use(logger);

app.use('/users', userRouter);
app.use('/auth', authRouter);

app.use((req, res) => {
  res.status(404).json({ error: 'Route not found' });
});

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: err.message || 'Internal Server Error'
  });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});
▶ Try it Yourself

Run Test:

BASH
node app.js
TEXT 📖 Display only
Server running on port 3000
GET /users 200 3ms
GET /users/1 200 2ms
POST /auth/login 200 5ms

❓ FAQ

Q What happens if next() isn't called?
A The request will be suspended, and the client will never receive a response. You must call next() in the middleware to pass control, or use methods like res.end() or res.json() to directly terminate the response.
Q What is the difference between route parameters and query parameters?
A Route parameters are part of the URL path, such as the 42 in /users/:id, which can be retrieved using req.params.id; query parameters are key-value pairs following the ?, such as /users?role=admin, which can be retrieved using req.query.role. The former identifies resources, while the latter filters resources.
Q Does the order in which middleware is registered matter?
A It matters a great deal. Express executes middleware in the order they are registered. For example, express.json() must be registered before any route that needs to read req.body; otherwise, req.body will be undefined.
Q How do I implement global error handling?
A Define a 4-argument function (err, req, res, next) and register it with app.use() after all routes and middleware. Any middleware that calls next(err) will be routed to this handler.
Q What problem does the CORS middleware solve?
A The browser's same-origin policy blocks cross-origin requests. The CORS middleware adds fields such as Access-Control-Allow-Origin to the response headers, allowing front-end applications from specified origins to access the API.
Q What is the difference between express.Router() and using app.get() directly?
A Router creates a standalone router instance that can be mounted to any route prefix, making it suitable for modularization; app.get() registers directly on the application instance, making it suitable for simple projects. Both are functionally equivalent; the difference lies in how they are organized.
Q How can I modify the req object in a middleware to pass data to subsequent processing?
A Simply attach properties directly to req, such as req.user = { id: 1 }. Subsequent middleware and routes can then access this data via req.user. This is a common pattern for communication between middleware in Express.

📖 Summary


📝 Exercises

  1. Complete all the code examples in this lesson and make sure each one runs correctly.
  2. Modify the comprehensive example and add your own extensions
  3. Review the official documentation, identify 1–2 APIs not covered in this lesson, and write test code for them.
  4. Reflection: How would you apply what you’ve learned in this lesson to a real-world project?
  5. Try to combine what you’ve learned in this lesson with material from previous lessons to build a small project.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏