Script Valley
REST API Development: Complete Course from Beginner to Production
Building REST APIs with Express.jsLesson 3.4

Project Structure: Scalable Architecture for REST APIs

MVC architecture, controllers, routes, models, services, middleware folder, project structure, separation of concerns, asyncHandler

Project Structure: Scalable Architecture for REST APIs

The way you structure your REST API project determines how easy it is to maintain, test, and onboard new developers. A flat pile of files in one directory works for a tutorial but falls apart in production.

DiagramScalable REST API Project Structure

IMAGE PROMPT (replace this block with your generated image):

Flat layered architecture diagram on white background. Title: Scalable REST API Architecture Layers. Five horizontal layers with left-to-right flow arrows showing a request traveling down and response traveling up. Layer 1 (top): Route Layer — pill badge reading routes/, description: Maps URLs to controllers. Fill: very light #3A5EFF (#e8ecff). Layer 2: Controller Layer — pill badge reading controllers/, description: Parses req, formats res. Fill: slightly deeper blue tint. Layer 3: Service Layer — pill badge reading services/, description: Business logic and rules. Fill: medium blue tint. Layer 4: Model Layer — pill badge reading models/, description: Mongoose schemas and queries. Fill: deeper blue tint. Layer 5 (bottom): Database — MongoDB cylinder icon. Plus a floating sidebar column on the right labeled Cross-Cutting Concerns with three cards: Middleware (auth, validation), Utils (asyncHandler, AppError), Config (dotenv, db connect). All connected to the main layers by dashed #3A5EFF lines. Clean sans-serif font, white background.

The MVC-Inspired Layer Architecture

Professional REST APIs separate concerns into distinct layers. The route layer defines URL paths and maps them to controllers. The controller layer handles request parsing and response formatting. The service layer contains business logic. The model layer defines data schemas and handles database operations. Middleware handles cross-cutting concerns like auth, validation, and logging.

Recommended Directory Structure

src/
  config/
    database.js
  middleware/
    auth.js
    validate.js
    errorHandler.js
  modules/
    users/
      user.routes.js
      user.controller.js
      user.service.js
      user.model.js
      user.validation.js
  utils/
    AppError.js
    asyncHandler.js
  index.js
  app.js

The asyncHandler Utility

Async route handlers need try-catch blocks in every function. An asyncHandler wrapper eliminates the repetition:

const asyncHandler = (fn) => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(next);

Wrap every async controller: router.get('/', asyncHandler(getAllUsers));. Any unhandled promise rejection is automatically forwarded to the error handling middleware.

Custom AppError Class

class AppError extends Error {
  constructor(message, statusCode, code) {
    super(message);
    this.statusCode = statusCode;
    this.code = code;
    this.isOperational = true;
  }
}

The isOperational flag lets your error handler distinguish between expected operational errors (404, 403, validation failures) and programmer bugs (null references, type errors).

Up next

Testing REST APIs: Unit Tests and Integration Tests

Sign in to track progress