Structuring Controllers

Move route logic into controller functions to keep route files clean.

Syntaxrouter.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

Example · javascript
// 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.

Example · js
// 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.

Example · js
// 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.

Example · js
// 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

  • Be the first to comment on this lesson.