Structuring Controllers
Move route logic into controller functions to keep route files clean.
Syntax
router.get('/', controller.index)As logic grows, keep it out of the route definitions. A controller is a module of functions, one per action. The route file simply wires URLs to controller functions.
This separation makes code easier to test and read: routes describe what URLs exist, controllers describe how each one behaves.
Example
// controllers/postsController.js
exports.index = (req, res) => res.json(posts);
exports.show = (req, res) => {
const post = posts.find(p => p.id === Number(req.params.id));
res.json(post);
};
// routes/posts.js
const router = require('express').Router();
const posts = require('../controllers/postsController');
router.get('/', posts.index);
router.get('/:id', posts.show);
module.exports = router;When to use it
- A developer extracts all user-related handler logic into a usersController.js file to keep the router file limited to route declarations.
- A team's controller functions are imported and unit-tested in isolation without spinning up the Express server.
- A controller calls a service layer function to fetch data from the database, keeping HTTP-specific logic out of business logic.
More examples
Controller module
Exports three named controller functions from a dedicated module, each handling one HTTP operation.
// controllers/users.js
const users = [];
exports.list = (req, res) => res.json(users);
exports.create = (req, res) => {
const user = { id: Date.now(), ...req.body };
users.push(user);
res.status(201).json(user);
};
exports.remove = (req, res) => {
const idx = users.findIndex(u => u.id == req.params.id);
if (idx === -1) return res.status(404).json({ error: 'Not found' });
users.splice(idx, 1);
res.status(204).send();
};Router using controller
The router file imports the controller and maps each route method to the corresponding exported function.
// routes/users.js
const express = require('express');
const router = express.Router();
const ctrl = require('../controllers/users');
router.get('/', ctrl.list);
router.post('/', ctrl.create);
router.delete('/:id', ctrl.remove);
module.exports = router;Controller calling a service
The controller delegates database work to a service layer and forwards unexpected errors to the Express error middleware.
// controllers/products.js
const productService = require('../services/productService');
exports.getById = async (req, res, next) => {
try {
const product = await productService.findById(req.params.id);
if (!product) return res.status(404).json({ error: 'Not found' });
res.json(product);
} catch (err) {
next(err);
}
};
Discussion