DevPath · Learn to code ESPTEN

Capstone: a complete REST API

The layers: repository, validation and handlers

Why separate into layers

If you cram data access, validation and HTTP logic into a single function, you end up with code that is impossible to test and maintain. A clean API separates responsibilities into layers, each with one job:

HTTP request
   │
   ▼
[ middleware ]   ── auth (who are you?) and validation (correct data?)
   │
   ▼
[ controller/handler ]  ── translates HTTP ↔ data: reads req, responds res
   │
   ▼
[ repository ]  ── reads and writes the store (here, an in-memory array)

The repository (data access)

Pure JavaScript functions that operate on the store. They know nothing about HTTP:

function create(tasks, title) {
  const task = { id: tasks.length + 1, title, done: false };
  tasks.push(task);
  return task;
}
function findById(tasks, id) {
  return tasks.find((t) => t.id === id);
}

The middleware (auth and validation)

They run before the handler. If something goes wrong, they respond and cut the chain; if everything is fine, they call next():

function requireAuth(req, res, next) {
  if (!req.headers.authorization) {
    res.status(401).json({ error: "Unauthorized" });
    return;
  }
  next();
}

The handler (controller)

It is the only one that touches req and res: it reads the input, calls the repository and chooses the status code and the response body:

function createTask(req, res) {
  const task = create(tasks, req.body.title);
  res.status(201).json(task);
}

In this module you will build each piece separately and check that it works in isolation. That is how you assemble an API that scales.

Put this into practice

DevPath is a hands-on course: you read the theory here; in the app you put it into practice with exercises that really run, offline.

Start free in the app →
← The plan: the tasks APIView the module →