Learn / Frameworks / Express / Layered Project Structure

Express · Lesson 13 of 15

Layered Project Structure

Split routes, services and data access so handlers stay thin.

  • Advanced
  • 15 min read
  • 3 objectives

Before this lessonLesson 12: WebSockets

What you will learn

  • Draw the layers
  • Keep SQL out of routes
  • Add a request id

Your Progress

0 of 15 lessons 0%

  • Lessons0 / 15
  • Completed0
  • Est. time left~ 4 hours

Create a free account to keep your progress on every device.

A growing Express app becomes unreadable when SQL, validation and HTTP live in one file. A simple layering is enough.

Folders

src/
  app.js            # middleware, mounts routers, error handler
  server.js         # listen
  routes/tasks.js
  services/tasks.js # business rules
  db/tasks.js       # SQL or Prisma
  middleware/auth.js
  lib/async.js

Thin handlers

// routes/tasks.js
router.post("/", validate(CreateTask), wrap(async (req, res) => {
  const task = await tasksService.create(req.userId, req.valid.body);
  res.status(201).json(task);
}));

// services/tasks.js
export async function create(userId, input) {
  return db.tasks.insert({ ...input, userId });
}

Services throw domain errors (err.status = 409); the HTTP layer maps them. Tests can hit services without supertest.

Up next · Lesson 14Logging and Request IdsStructured logs with pino, a request id, and what never to log.