What are controllers in Express applications?
In short:
A controller is where the main logic of processing the request and building the response to the client happens.
1. What a controller is in Express
In Express.js, a controller is a function (or set of functions) that gets called when a user hits a particular route.
It:
- receives the
req(request) andres(response) objects; - calls the needed services, models, or databases;
- returns a response (JSON, HTML, a file, etc.) to the user;
- handles errors when needed.
A simple controller example
javascript
// controllers/userController.js
exports.getAllUsers = async (req, res, next) => {
try {
const users = await User.find(); // reach out to the model
res.json(users); // send the response to the client
} catch (err) {
next(err); // pass the error to middleware
}
};And mounting this controller inside a route:
javascript
// routes/users.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
router.get('/', userController.getAllUsers);
module.exports = router;Now GET /users calls the getAllUsers() function from the controller.
2. What a controller does
| Stage | What it does |
|---|---|
| Receives data | Reads request parameters: req.params, req.query, req.body, req.headers |
| Calls business logic | Through services or models (database work, checking conditions, authorization, etc.) |
| Builds the response | Sends the client res.json(), res.status(), res.send(), res.redirect(), etc. |
| Handles errors | Via try...catch and next(err), passing them to error middleware |
3. Why controllers are needed
| Reason | Explanation |
|---|---|
| Separation of concerns | The controller handles requests, not routing. |
| Cleaner code | The code in routes stays minimal and easy to follow. |
| Reuse | The same controller can be mounted into different routes or projects. |
| Easier testing | Controllers can be tested independently of the Express application. |
| Scalability | As the API grows, controllers can be split across modules (users, products, orders…). |
4. An example Express application structure with controllers
javascript
src/
├── app.js
├── routes/
│ ├── users.js
│ ├── products.js
│ └── auth.js
├── controllers/
│ ├── userController.js
│ ├── productController.js
│ └── authController.js
├── services/
│ ├── userService.js
│ └── productService.js
├── middlewares/
│ ├── authMiddleware.js
│ └── errorHandler.js
└── db/
└── index.jsHere:
routes/only defines the paths (/users,/products, etc.);controllers/holds the functions that run when those routes are hit;services/holds the business logic (DB work, APIs, etc.).
5. A production-grade controller example
javascript
// controllers/productController.js
const productService = require('../services/productService');
exports.createProduct = async (req, res, next) => {
try {
const product = await productService.create(req.body);
res.status(201).json(product);
} catch (err) {
next(err); // centralized error handling
}
};
exports.getProduct = async (req, res, next) => {
try {
const product = await productService.getById(req.params.id);
if (!product) return res.status(404).json({ message: 'Not found' });
res.json(product);
} catch (err) {
next(err);
}
};6. Summary
Controllers are functions that do the main work of handling an HTTP request: they receive data from the client, call business logic, and return a response.
Their main purpose is to:
- separate routes from logic;
- make the code structured, readable, and scalable;
- allow handlers to be tested and reused independently of Express.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.