Project: Production REST API
📖 Concept
Build a complete, production-ready REST API from scratch, applying everything learned across all previous phases. This project integrates Express.js, databases, authentication, testing, Docker, and CI/CD into a single cohesive application.
Project: Task Management API (like Trello/Jira)
Features:
- User registration and JWT authentication
- Workspaces with role-based access (owner, admin, member)
- Boards with lists and cards (Kanban style)
- Card assignments, labels, due dates, comments
- File attachments (upload to S3)
- Activity log (who did what when)
- Real-time updates via WebSockets
- Full-text search across cards and comments
Tech stack:
Backend: Express.js + TypeScript
Database: PostgreSQL (Prisma ORM)
Cache: Redis (sessions, cache, rate limiting)
Auth: JWT + refresh tokens
Files: AWS S3 / MinIO
Queue: BullMQ (email notifications, file processing)
Testing: Jest + Supertest
Deploy: Docker + GitHub Actions CI/CD
Monitor: Prometheus + Grafana
Architecture:
Client → nginx → Express API → PostgreSQL
↕ ↕
Redis BullMQ Workers
↕
Socket.IO
This project tests your ability to:
- Design a clean, maintainable project structure
- Implement complex business logic with proper error handling
- Write comprehensive tests (unit + integration)
- Deploy with Docker and CI/CD
- Monitor and debug in production
🏠 Real-world analogy: This is the capstone project — like building a fully furnished house. You've learned about foundations (Node.js core), framing (Express), plumbing (databases), electrical (auth), and painting (frontend). Now you put it all together into a house someone can actually live in.
💻 Code Example
1// Production REST API — Project Structure & Core Implementation23// === Project structure ===4// src/5// ├── config/ → Environment, database, Redis config6// ├── middleware/ → Auth, validation, error handling, rate limiting7// ├── modules/ → Feature modules (users, boards, cards)8// │ ├── users/9// │ │ ├── user.controller.js10// │ │ ├── user.service.js11// │ │ ├── user.repository.js12// │ │ ├── user.routes.js13// │ │ └── user.validation.js14// │ ├── boards/15// │ └── cards/16// ├── shared/ → Shared utilities, base classes17// ├── jobs/ → BullMQ job processors18// ├── app.js → Express app setup19// └── server.js → Entry point (app.listen)20// tests/21// ├── unit/22// ├── integration/23// └── fixtures/24// prisma/25// ├── schema.prisma26// └── migrations/27// docker-compose.yml28// Dockerfile29// .github/workflows/ci.yml3031// === src/app.js — Application Setup ===32const express = require("express");33const helmet = require("helmet");34const cors = require("cors");35const rateLimit = require("express-rate-limit");3637function createApp(dependencies) {38 const app = express();3940 // Security41 app.use(helmet());42 app.use(cors({ origin: process.env.CORS_ORIGIN, credentials: true }));43 app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));44 app.use(express.json({ limit: "10mb" }));4546 // Request ID47 app.use((req, res, next) => {48 req.requestId = req.headers["x-request-id"] || require("crypto").randomUUID();49 res.setHeader("X-Request-Id", req.requestId);50 next();51 });5253 // Health check54 app.get("/health", async (req, res) => {55 res.json({56 status: "healthy",57 version: process.env.npm_package_version,58 uptime: process.uptime(),59 });60 });6162 // API routes63 app.use("/api/v1/auth", dependencies.authRoutes);64 app.use("/api/v1/users", dependencies.userRoutes);65 app.use("/api/v1/boards", dependencies.boardRoutes);66 app.use("/api/v1/cards", dependencies.cardRoutes);6768 // 40469 app.use((req, res) => {70 res.status(404).json({ error: `${req.method} ${req.path} not found` });71 });7273 // Error handler74 app.use((err, req, res, next) => {75 const statusCode = err.statusCode || 500;76 const message = err.isOperational ? err.message : "Internal server error";7778 console.error({79 requestId: req.requestId,80 error: err.message,81 stack: err.stack,82 path: req.path,83 });8485 res.status(statusCode).json({86 success: false,87 error: message,88 requestId: req.requestId,89 });90 });9192 return app;93}9495// === src/modules/cards/card.service.js ===96class CardService {97 constructor(cardRepo, boardService, notificationQueue, cache) {98 this.cardRepo = cardRepo;99 this.boardService = boardService;100 this.notificationQueue = notificationQueue;101 this.cache = cache;102 }103104 async createCard(boardId, data, userId) {105 // Verify board access106 const board = await this.boardService.getBoard(boardId, userId);107 if (!board) throw new AppError("Board not found", 404);108109 const card = await this.cardRepo.create({110 ...data,111 boardId,112 creatorId: userId,113 position: await this.cardRepo.getNextPosition(boardId, data.listId),114 });115116 // Invalidate cache117 await this.cache.del(`board:${boardId}:cards`);118119 // Notify assigned users120 if (data.assigneeIds?.length) {121 await this.notificationQueue.add("card-assigned", {122 cardId: card.id,123 assigneeIds: data.assigneeIds,124 assignedBy: userId,125 });126 }127128 return card;129 }130131 async moveCard(cardId, targetListId, position, userId) {132 const card = await this.cardRepo.findById(cardId);133 if (!card) throw new AppError("Card not found", 404);134135 await this.boardService.verifyAccess(card.boardId, userId);136137 const updated = await this.cardRepo.transaction(async (tx) => {138 // Reorder cards in source and target lists139 await this.cardRepo.reorderAfterRemove(card.listId, card.position, tx);140 await this.cardRepo.reorderAfterInsert(targetListId, position, tx);141142 return this.cardRepo.update(cardId, {143 listId: targetListId,144 position,145 }, tx);146 });147148 await this.cache.del(`board:${card.boardId}:cards`);149 return updated;150 }151152 async searchCards(query, userId) {153 const boards = await this.boardService.getUserBoards(userId);154 const boardIds = boards.map((b) => b.id);155156 return this.cardRepo.fullTextSearch(query, boardIds);157 }158}159160class AppError extends Error {161 constructor(message, statusCode) {162 super(message);163 this.statusCode = statusCode;164 this.isOperational = true;165 }166}167168module.exports = { createApp, CardService, AppError };
🏋️ Practice Exercise
Exercises:
- Build the complete Task Management API with users, boards, lists, and cards
- Implement role-based access: workspace owners can manage members; members can only view/edit assigned cards
- Add real-time updates — when a card is moved, all connected users see the change instantly
- Implement full-text search with PostgreSQL
tsvectoror Elasticsearch - Write integration tests for the complete API — achieve 80%+ code coverage
- Dockerize the application and deploy with CI/CD — include database migrations in the pipeline
⚠️ Common Mistakes
Not designing the project structure upfront — jumping into code without architecture leads to spaghetti code; plan modules and layers first
Mixing concerns in route handlers — controllers should only handle HTTP; business logic belongs in services; data access in repositories
Skipping error handling for edge cases — real users will send unexpected data; test with invalid, missing, and malformed inputs
Not writing tests during development — retrofitting tests is harder and less effective; write tests alongside features
Deploying without monitoring — you can't fix what you can't measure; set up logging, metrics, and error tracking from day one
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Project: Production REST API