Roadmap › Node.js — Runtime Internals, Express, APIs and SQL Server › Module: Building APIs
Lessons in this course (11)
7. Node.js / Express project folder structure (with sample files)
Basic★ Must knowLesson 7 of 11
text
shop-api/
├── src/
│ ├── server.js starts the HTTP server, graceful shutdown
│ ├── app.js creates the Express app: middleware, routes, error handler (exported for tests)
│ ├── config/
│ │ └── index.js reads environment variables (dotenv / process.env) and validates them
│ ├── db/
│ │ ├── pool.js SQL Server connection pool (mssql)
│ │ └── migrations/ SQL scripts or Prisma migrations
│ ├── modules/ feature folders (vertical slices)
│ │ ├── products/
│ │ │ ├── product.routes.js Express router: URL → controller
│ │ │ ├── product.controller.js HTTP layer: read req, call service, send res
│ │ │ ├── product.service.js business logic
│ │ │ ├── product.repository.js SQL queries
│ │ │ ├── product.schema.js zod validation schemas
│ │ │ └── product.test.js
│ │ └── orders/ …
│ ├── middleware/
│ │ ├── auth.js JWT verification
│ │ ├── validate.js validation middleware
│ │ └── errorHandler.js
│ └── utils/
│ └── logger.js pino/winston logger
├── tests/ integration tests (supertest)
├── .env.example documented environment variables (never commit .env)
├── .gitignore node_modules, .env, dist
├── Dockerfile
├── package.json / package-lock.json
└── README.mdjs
// src/app.js
import express from 'express';
import { productRouter } from './modules/products/product.routes.js';
import { errorHandler } from './middleware/errorHandler.js';
export const app = express();
app.use(express.json());
app.use('/api/products', productRouter);
app.use(errorHandler);
// src/modules/products/product.routes.js
import { Router } from 'express';
import * as controller from './product.controller.js';
export const productRouter = Router();
productRouter.get('/', controller.list);
productRouter.get('/:id', controller.getById);
productRouter.post('/', controller.create);
// src/modules/products/product.controller.js
import * as service from './product.service.js';
export async function list(req, res) { res.json(await service.list(Number(req.query.page ?? 1))); }
export async function getById(req, res) {
const product = await service.getById(Number(req.params.id));
return product ? res.json(product) : res.status(404).json({ title: 'Not found' });
}
export async function create(req, res) { res.status(201).json(await service.create(req.body)); }
// src/server.js
import { app } from './app.js';
import { config } from './config/index.js';
const server = app.listen(config.port, () => console.log(`API on :${config.port}`));
process.on('SIGTERM', () => server.close());NestJS projects use a similar feature structure with *.module.ts, *.controller.ts, *.service.ts and decorators (Angular-style DI).