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 { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { InjectRepository } from '@nestjs/typeorm';
A cache-aside country lookup in NestJS
cache-aside
dependency-injection
batch-lookup
Intermediate
8 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
typescript
import { Injectable, effect, signal, computed } from '@angular/core'; interface Preferences { theme: 'light' | 'dark';
A signal-based preferences store in Angular
signals
state-management
persistence
Intermediate
7 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.