Folder Structure
Standard Express layout — routes, controllers, services, and middleware.
Every Express service follows the same folder structure. Consistency makes it easy to navigate any backend repo.
Project layout
backend-api/
├── src/
│ ├── server.ts # Entry point — starts HTTP server
│ ├── app.ts # Express app factory (used in tests too)
│ │
│ ├── routes/ # Route definitions (thin — delegate to controllers)
│ │ ├── health.routes.ts
│ │ └── v1/
│ │ ├── index.ts # Mounts all v1 routers
│ │ ├── users.routes.ts
│ │ └── projects.routes.ts
│ │
│ ├── controllers/ # Request/response handling
│ │ ├── users.controller.ts
│ │ └── projects.controller.ts
│ │
│ ├── services/ # Business logic (no Express types here)
│ │ ├── users.service.ts
│ │ ├── projects.service.ts
│ │ ├── push.service.ts # FCM send helpers
│ │ ├── uploads.service.ts # S3 presigned URLs
│ │ └── email/ # Email provider adapters
│ │ ├── index.ts # Factory (ses | sendgrid | brevo)
│ │ ├── ses.provider.ts
│ │ ├── sendgrid.provider.ts
│ │ └── brevo.provider.ts
│ │
│ ├── middleware/ # Express middleware
│ │ ├── auth.middleware.ts
│ │ ├── rate-limit.middleware.ts
│ │ ├── validate.middleware.ts
│ │ └── error.middleware.ts
│ │
│ ├── models/ # Sequelize / Mongoose models
│ │ └── user.model.ts
│ │
│ ├── db/ # Database config & Drizzle schema
│ │ ├── index.ts # Connection pool
│ │ ├── schema/ # Drizzle schema files
│ │ └── migrations/ # Drizzle / Sequelize migrations
│ │
│ ├── socket/ # Socket.io setup
│ │ ├── index.ts # IO server init
│ │ ├── auth.middleware.ts # Socket auth
│ │ └── handlers/ # Event handlers per domain
│ │ └── notifications.handler.ts
│ │
│ ├── lib/ # Shared utilities
│ │ ├── cognito.ts # JWT verification
│ │ ├── supabase.ts # Supabase client singleton
│ │ ├── s3.ts # AWS S3 client
│ │ ├── fcm.ts # Firebase Admin init
│ │ └── logger.ts
│ │
│ ├── types/ # Shared TypeScript types & Express augmentations
│ │ ├── express.d.ts # req.user augmentation
│ │ └── api.types.ts
│ │
│ └── validators/ # Zod schemas for request validation
│ ├── users.validator.ts
│ └── projects.validator.ts
│
├── tests/
│ ├── unit/
│ └── integration/
│
├── .env.example
├── docker-compose.yml
├── Dockerfile
├── tsconfig.json
└── package.jsonLayer responsibilities
| Layer | Responsibility | Must NOT |
|---|---|---|
| routes/ | Define paths, attach middleware, call controller | Contain business logic |
| middleware/ | Auth, validation, rate limiting, logging | Call database directly |
| controllers/ | Parse request, call service, format response | Contain complex business rules |
| services/ | Business logic, orchestration | Import Express Request/Response |
| models/ · db/ | Data access, schema definitions | Handle HTTP concerns |
Route example
// src/routes/v1/projects.routes.ts
import { Router } from "express";
import { authenticate } from "../../middleware/auth.middleware";
import { requireScope } from "../../middleware/scope.middleware";
import { validate } from "../../middleware/validate.middleware";
import { createProjectSchema } from "../../validators/projects.validator";
import * as projectsController from "../../controllers/projects.controller";
const router = Router();
router.get("/", authenticate, projectsController.list);
router.post(
"/",
authenticate,
requireScope("write:projects"),
validate(createProjectSchema),
projectsController.create,
);
export default router;Controller example
// src/controllers/projects.controller.ts
import { Request, Response, NextFunction } from "express";
import * as projectsService from "../services/projects.service";
export async function list(req: Request, res: Response, next: NextFunction) {
try {
const projects = await projectsService.listByUser(req.user!.sub);
res.json({ data: projects });
} catch (err) {
next(err);
}
}Service example
// src/services/projects.service.ts
import { ProjectModel } from "../models/project.model";
export async function listByUser(userId: string) {
return ProjectModel.findAll({ where: { ownerId: userId } });
}Naming conventions
| Item | Convention | Example |
|---|---|---|
| Route files | {resource}.routes.ts | users.routes.ts |
| Controllers | {resource}.controller.ts | users.controller.ts |
| Services | {resource}.service.ts | users.service.ts |
| Validators | {resource}.validator.ts | users.validator.ts |
| Socket handlers | {domain}.handler.ts | notifications.handler.ts |