NestJS Production Starter
A starting point for new APIs: JWT with rotating refresh tokens, roles, Swagger, Docker, tests and CI.
- Type
- Side project
- Tech stack
- NestJS
- JWT
- Swagger
- Docker
- CI
Why
Every backend needs the same groundwork: auth, validation, config, logging, migrations, API docs, containers and CI. Doing it properly takes days, and doing it in a hurry leads to shortcuts that hurt later. This starter is that groundwork, done once and tested, ready to clone and build on.
What's included
| Area | Included |
|---|---|
| Auth | JWT access tokens with rotating refresh tokens, passwords hashed with bcrypt |
| Roles | @Roles('admin') guards and a @CurrentUser() decorator |
| Validation | A global ValidationPipe with class-validator DTOs, whitelisting and transformation |
| Database | PostgreSQL with Prisma, migrations and a seed script |
| Docs | Swagger/OpenAPI at /docs, including auth |
| Config | Typed environment variables, validated at startup |
| Errors | One global exception filter with one consistent error shape |
| Security | Helmet, CORS config and rate limiting with @nestjs/throttler |
| Operations | A /health endpoint for app and database, structured JSON logs with request IDs |
| Tests | Unit tests with Jest, e2e tests with Supertest against a real test database |
| Delivery | Multi-stage Dockerfile, docker compose for app and Postgres, GitHub Actions for lint, type check, tests and build |
Request flow
- ClientHTTP
- GuardsJWT · roles · throttle
- ValidationPipe
- Controllers
- Services
- PostgreSQLPrisma
Choices worth explaining
Config is validated when the app starts. A missing secret stops it right away instead of causing odd errors in production.
Access tokens are short-lived. Refresh tokens are rotated and invalidated on logout. That's quick to describe and takes care to get right.
The e2e tests run against a separate, real database instead of mocks, because mocks hide problems with constraints and transactions.
The starter also provides the login for AgentDesk. Using it in a second project showed which parts actually need to be configurable.