Advanced Controllers & Logic

As your API grows, putting logic directly inside route files becomes a nightmare. Master the art of Decoupling.

1. The Route vs The Controller

The route file should only define where requests go. The controller handles what happens.

// routes/userRoutes.js
router.get('/:id', userController.getUser);

// controllers/userController.js
exports.getUser = async (req, res, next) => {
  const user = await UserService.findById(req.params.id);
  res.json({ data: { user } });
};

2. Higher-Order Controllers

For standard CRUD, don't repeat yourself. Use a generic handler factory.

const deleteOne = (Model) => async (req, res) => {
  await Model.findByIdAndDelete(req.params.id);
  res.status(204).json({ data: null });
};

// Usage
exports.deleteUser = deleteOne(User);
exports.deleteProduct = deleteOne(Product);

3. Business Logic vs DB Logic

Don't put complex business rules in your controllers. Move them to aService Layer. This makes your API logic testable without a database.

Scalability Tip: By keeping your controllers "thin" (only handling input and output), you can switch from one framework (e.g., Express) to another (e.g., Fastify) with minimal changes to your core logic.

Explore Related Tools

Hand-picked utilities and calculators related to this tool.

Related Tools