Chapters of From Frontend to Full-Stack Backend

Week 1 - API FoundationChapter 2 of 282 min read

Project structure and the middleware chain - keeping big codebases tidy

Separating routes, controllers, services, middleware order, next() and error handlers

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

Each 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 end

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

All chapters of From Frontend to Full-Stack Backend