Everyone makes one mistake at the start - writing every route, every piece of logic, every database query in a single file. It works at first, but once you have 10 endpoints it becomes hard to find which code lives where. Just like you keep frontend components separate, the backend has the same rule.
src/
routes/user.routes.ts
controllers/user.controller.ts
services/user.service.ts
middlewares/auth.middleware.tsEach one has a separate job. Routes only say which controller runs on which URL. The controller only takes the request and sends the response, nothing more. The real work - fetching data from the database, doing calculations, checking rules - is done by the service. That gives two benefits: the same service can be reused from elsewhere later, and testing becomes easy.
Middleware - checkpoints on the request's path
Middleware means the steps before a request reaches the controller. One after another the request moves forward - one logs, one checks login, one validates data. When each middleware finishes its work it must call next(), otherwise the request gets stuck right there.
function logger(req, res, next) {
console.log(req.method, req.url);
next();
}
app.use(logger);
app.use("/users", userRoutes);
app.use(errorHandler); // always at the very endThe order matters a lot here. If the logger is first, you get a record of every request. Auth middleware must sit before the specific route, otherwise data goes out without any login. And the error handler is always last - because Express runs top to bottom, and errors get caught at that final step below.
The mistake everyone makes
Three mistakes show up again and again: writing the auth check after the route (then the check does nothing at all), putting the error handler at the top (errors below never reach it), and forgetting next() in a middleware (the request hangs on the frontend and no response ever comes).
Today's practice
Split the 3 endpoints from chapter 1 into this structure - routes, controllers, services in separate files. Add a logger middleware. And keep an error handler at the end, so that a wrong route gives a clean 404 response.
How to say it in an interview
I keep routes, controllers and services separate, so each layer's responsibility stays clear and the code stays easy to maintain. I arrange middleware order deliberately - logger first, auth after that, error handling at the very end, because Express executes from top to bottom.