typescript
53 lines · 8 steps
Wiring validation and Swagger docs in NestJS
A DTO's decorators drive both runtime validation and generated OpenAPI docs from one source of truth.
Explained by
highlit
1import { NestFactory } from '@nestjs/core';
2import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
3import { ValidationPipe } from '@nestjs/common';
4import { ApiProperty } from '@nestjs/swagger';
5import { IsEmail, IsInt, IsOptional, Min } from 'class-validator';
6import { Type } from 'class-transformer';
7import { AppModule } from './app.module';
8
9export class CreateUserDto {
10 @ApiProperty({ example: 'ada@example.com', description: 'Unique login email' })
11 @IsEmail()
12 email: string;
13
14 @ApiProperty({ example: 'Ada Lovelace', minLength: 2, maxLength: 120 })
15 fullName: string;
16
17 @ApiProperty({ example: 34, minimum: 0, description: 'Age in whole years' })
18 @Type(() => Number)
19 @IsInt()
20 @Min(0)
21 age: number;
22
23 @ApiProperty({
24 required: false,
25 enum: ['admin', 'member', 'guest'],
26 default: 'member',
27 })
28 @IsOptional()
29 role?: 'admin' | 'member' | 'guest';
30}
31
32async function bootstrap() {
33 const app = await NestFactory.create(AppModule);
34
35 app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
36
37 const config = new DocumentBuilder()
38 .setTitle('Users API')
39 .setDescription('CRUD endpoints for managing platform users')
40 .setVersion('1.0.0')
41 .addBearerAuth()
42 .addTag('users')
43 .build();
44
45 const document = SwaggerModule.createDocument(app, config);
46 SwaggerModule.setup('docs', app, document, {
47 swaggerOptions: { persistAuthorization: true },
48 });
49
50 await app.listen(process.env.PORT ?? 3000);
51}
52
53bootstrap();
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Decorators let one DTO class serve as both the validation contract and the API documentation.
- 2A global ValidationPipe with whitelist and transform strips unknown fields and coerces types automatically.
- 3Swagger's DocumentBuilder reads your decorators to produce a live, interactive API spec at runtime.
Related explainers
typescript
import { Component, Input } from '@angular/core'; interface Order { id: string;
How Angular ICU plurals localize an order summary
i18n
pluralization
standalone-component
Intermediate
8 steps
typescript
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable, catchError, concatMap, finalize } from 'rxjs'; import { DataSource, QueryRunner } from 'typeorm';
Wrapping requests in a transaction with NestJS
interceptors
transactions
rxjs
Advanced
7 steps
python
from functools import wraps import asyncio from fastapi import APIRouter, FastAPI, Request
Per-route request timeouts in FastAPI
decorators
async
timeouts
Intermediate
6 steps
typescript
type CsvColumn<T> = { header: string; value: (row: T) => string | number | boolean | null | undefined; };
Building a type-safe CSV writer in TypeScript
generics
serialization
escaping
Intermediate
7 steps
typescript
import { parsePhoneNumberFromString, CountryCode } from 'libphonenumber-js'; export interface NormalizedPhone { e164: string;
Normalizing phone numbers to E.164 in TypeScript
validation
normalization
error-handling
Intermediate
7 steps
php
<?php namespace App\Http\Controllers;
Broadcasting typing indicators in Laravel
websockets
authorization
presence-channels
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/wiring-validation-and-swagger-docs-in-nestjs-explained-typescript-4f88/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.