NestJS REST API Best Practices That Scale
Module boundaries, DTO validation, guards, exception filters and configuration: the NestJS structure that stays clean as your API grows.
Configuration changes between environments; code should not. Environment variables keep them apart.
Store values such as database URLs, API keys and JWT secrets in the environment and read them with process.env. Use a .env file locally, loaded by dotenv or your framework.
Add .env to .gitignore and commit a .env.example listing the variable names with fake values. New contributors then know exactly what to set.
Fail fast when something is missing or malformed instead of crashing later in production.
import { z } from 'zod';
const env = z.object({
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
PORT: z.coerce.number().default(5000),
}).parse(process.env);
Only variables prefixed with NEXT_PUBLIC_ are exposed to the browser. Never put a secret in one of those; anyone can read it in the page source.
Use different databases, secrets and API keys for local, staging and production. Hosting platforms such as Vercel let you set variables per environment in the dashboard.
A clean pattern is to keep both sets in the file and comment out the one you are not using, so switching never means retyping values.
Give each secret the least privilege it needs, rotate it on a schedule and immediately if it leaks. If a secret ever reaches git history, treat it as compromised even after deleting the file.
Config in the environment, secrets out of git, validation at startup and separate values per environment.
Module boundaries, DTO validation, guards, exception filters and configuration: the NestJS structure that stays clean as your API grows.
Schema design, migrations, relations, transactions and performance tips for using Prisma with PostgreSQL, MySQL or MongoDB.
Implement secure login with short-lived access tokens, rotating refresh tokens and httpOnly cookies, and avoid the common JWT mistakes.