Roadmap / NestJS Deep Dive
A comprehensive NestJS roadmap covering modules, controllers, providers, middleware, pipes, guards, interceptors, exception filters, database integration with Prisma, authentication with JWT, configuration management, testing, WebSockets, microservices, and production deployment.
Step 1 • Before You Start
NestJS is heavily TypeScript-first and uses patterns from Angular — decorators, dependency injection, and a modular architecture. You need strong TypeScript knowledge: decorators (@Get(), @Injectable() are decorators), interfaces, generics, enums, access modifiers (private/public/protected), and async/await. Node.js fundamentals: event loop, HTTP module, npm/package.json. OOP concepts: classes, inheritance, interfaces as contracts. Basic REST API and HTTP concepts. If you've used Angular or Spring Boot, NestJS will feel familiar — it's deliberately inspired by both. If you're coming from Express, expect more structure and boilerplate, in exchange for scalability and testability.
Step 2 • Setup
NestJS CLI scaffolds and manages the project. npm i -g @nestjs/cli, nest new project-name, nest generate (or nest g) controller/service/module/pipe/guard. Project structure: src/app.module.ts (root module), src/main.ts (bootstrap — creates NestApplication from the root module, calls app.listen()). Each feature lives in its own directory (users/, posts/) with a module, controller, and service. Use strict TypeScript (tsconfig.json). Understand: NestJS wraps either Express (default) or Fastify under the hood — you get either's performance characteristics. Enable CORS, set global prefixes (/api/v1), configure Swagger with @nestjs/swagger. Prettier + ESLint from the CLI template.
Step 3 • Foundations
@Controller('users') sets the route prefix. HTTP method decorators: @Get(), @Post(), @Put(), @Patch(), @Delete(). Route params: @Param('id') id: string. Query strings: @Query() query: QueryDto. Request body: @Body() body: CreateUserDto. Headers: @Headers('authorization'). Full request/response objects: @Req(), @Res() (avoid @Res — bypasses NestJS response handling). Return values from handlers become the JSON response automatically. @HttpCode(201) changes the status code. @Header('key', 'value') sets a response header. Versioning: URI versioning (@Controller({ version: '1' })). Route wildcards and optional parameters.
Step 4 • Foundations
@Injectable() marks a class as a provider — NestJS's DI container manages its lifecycle. Providers are injected via constructor: constructor(private readonly usersService: UsersService) — TypeScript reflection provides the type token. Register in the module's providers array. Scope: DEFAULT (singleton — one instance per module, cached), REQUEST (new instance per request), TRANSIENT (new instance each time injected). Custom providers: useClass, useValue (inject a constant), useFactory (async factory for database connections), useExisting (alias). forwardRef() resolves circular dependencies (use sparingly — often indicates a design problem). inject tokens as strings or symbols (InjectionToken) for non-class providers.
Step 5 • Architecture
@Module({ imports, controllers, providers, exports }) defines a feature module. imports makes another module's exported providers available. exports makes this module's providers available to other modules that import it. Global modules (@Global()) register providers in all modules — use sparingly (logging, config). Dynamic modules (forRoot/forRootAsync patterns) configure a module with options (like @nestjs/config or @nestjs/typeorm). The root AppModule imports all feature modules. Module re-exporting (import a module and include it in exports). Shared modules — create a SharedModule that exports commonly used providers (config, logging) and import it everywhere. Module files are the architecture map of your app.
Step 6 • Input Handling
DTOs (Data Transfer Objects) define the shape of request/response data. ValidationPipe is NestJS's built-in pipe — apply globally (app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }))) to automatically validate and transform request bodies. Use class-validator decorators on DTO classes (@IsString(), @IsEmail(), @MinLength(8), @IsOptional(), @IsEnum(), @IsArray(), @ValidateNested() for nested objects, @Type(() => NestedDto)). whitelist: true strips properties not in the DTO. transform: true auto-converts types (string '1' from URL param to number 1). class-transformer for @Exclude() and @Expose() in responses. Custom pipes for specific transformations.
Step 7 • Security
Guards decide whether a request should proceed based on some condition — typically authentication and authorization. Implement the CanActivate interface — canActivate(context: ExecutionContext): boolean | Promise<boolean>. ExecutionContext gives access to the request. Apply with @UseGuards(AuthGuard) on a controller or method, or globally. JWT authentication: @nestjs/passport + passport-jwt — JwtStrategy extends PassportStrategy, validate() is called with decoded token payload. Roles-based authorization — @Roles('admin') custom decorator + RolesGuard reads the decorator with Reflector.getAllAndOverride(). @Public() decorator to opt out of global auth guard. @nestjs/jwt for signing and verifying tokens.
Step 8 • Cross-Cutting Concerns
Interceptors wrap the request/response cycle — they run before and after the handler. Use NestInterceptor interface with intercept(context, next) — call next.handle() to proceed, map the result. Common uses: response transformation (wrap all responses in { data: ..., timestamp: ... }), logging execution time, caching, and converting exceptions. Exception filters catch thrown exceptions and return structured error responses. ExceptionFilter interface — catch(exception, host: ArgumentsHost). @Catch(HttpException) handles HTTP exceptions. @Catch() (no args) catches everything. Apply globally: app.useGlobalFilters(new HttpExceptionFilter()). Built-in HttpException and its subclasses (NotFoundException, BadRequestException, UnauthorizedException, etc.).
Step 9 • intermediate
Handle errors declaratively with NestJS exception filters. Use built-in HttpException and its subclasses (NotFoundException, UnauthorizedException, BadRequestException, etc.). Create custom exception classes extending HttpException or BaseException. Implement @Catch() filters to intercept specific exception types and return structured error responses. Register filters at method, controller, or global scope. Build a global exception filter that logs errors to an observability system and returns consistent error envelopes.
Step 10 • Database
Prisma is the recommended ORM for NestJS — type-safe, schema-first, excellent TypeScript integration. Setup: npm install prisma @prisma/client, npx prisma init (creates prisma/schema.prisma), define models in the schema, npx prisma migrate dev (creates and runs migration). Inject PrismaService (wraps PrismaClient, extends OnModuleInit for connect/disconnect). Prisma Client is fully typed — prisma.user.findMany({ where: { email: ... }, include: { posts: true } }). Relations: one-to-many, many-to-many. Transactions: prisma.$transaction([...]). Prisma Studio for GUI browsing. TypeORM is the alternative (Active Record or Data Mapper pattern, supported natively in NestJS docs). Choose Prisma for new projects.
Step 11 • Configuration
@nestjs/config provides a ConfigModule (forRoot reads .env files via dotenv) and injects ConfigService for type-safe config access. ConfigModule.forRoot({ isGlobal: true }) makes ConfigService available everywhere without re-importing. Namespaced configs (registerAs) group related settings — databaseConfig, jwtConfig. Joi or Zod for validation of environment variables at startup (validationSchema option) — fail fast if required vars are missing. Don't use process.env directly in services (untestable) — always inject ConfigService. Separate .env, .env.development, .env.test, .env.production. Never commit .env files — use .env.example as documentation.
Step 12 • Testing
NestJS ships with Jest. Unit tests use Test.createTestingModule to create a minimal NestJS module with mocked dependencies. Mock services with jest.fn() or with a custom mock provider (useValue: { findOne: jest.fn().mockResolvedValue(user) }). Test one class in isolation — stub all dependencies. E2E tests use @nestjs/testing + supertest — bootstrap the full NestJS application and send real HTTP requests. Use a separate test database (Prisma: DATABASE_URL pointing to a test PostgreSQL database, run migrations in beforeAll). pactum for more readable HTTP assertions. Test the full request lifecycle including guards, pipes, and interceptors.
Step 13 • intermediate
Generate interactive API documentation with @nestjs/swagger. Use decorators to describe endpoints: @ApiOperation, @ApiResponse, @ApiParam, @ApiQuery, @ApiBody. Annotate DTOs with @ApiProperty (including type, description, example, required). Configure SwaggerModule.setup() to expose the Swagger UI and download the OpenAPI spec as JSON/YAML. Use @ApiBearerAuth() for JWT-protected endpoints. Automate API client SDK generation from the OpenAPI spec.
Step 14 • Real-Time
@nestjs/websockets provides WebSocket support via @WebSocketGateway(), @SubscribeMessage('event'), @WebSocketServer(). Uses Socket.io (default) or ws adapter. Gateways are providers — they can inject services. Connect the gateway to the main HTTP server or run on a separate port. @ConnectedSocket() injects the socket instance. Broadcast to all clients with this.server.emit() or to a specific room with this.server.to(room). Room management for group messaging. JWT authentication for WebSocket connections — validate the token in the handleConnection() lifecycle hook. Guard WebSocket events with the same guards as HTTP handlers. Scale WebSocket with Redis adapter (@nestjs/platform-socket.io + socket.io-redis).
Step 15 • Production
Production NestJS: Helmet (@nestjs/platform-express + helmet — sets security HTTP headers). Rate limiting with @nestjs/throttler — @Throttle({ default: { limit: 10, ttl: 60000 } }) globally, override per endpoint. Compression (compression middleware). Health checks with @nestjs/terminus — /health endpoint with database, memory, and disk checks (used by Kubernetes readiness/liveness probes). Logging with @nestjs/common Logger or integrate Winston/Pino for structured JSON logs. Docker: multi-stage Dockerfile (build stage with TypeScript compilation, runtime stage with only dist/ and node_modules). CI/CD with GitHub Actions. Deploy to Railway, Render, or AWS ECS.
Privacy choices
We use optional analytical tools only if you accept. You can change this later from "Privacy settings" in the footer.