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.md
js
// 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).