NestJS Controller and Swagger Guide

Introduction

NestJS is a progressive Node.js framework built with TypeScript, facilitating the development of efficient and scalable server-side applications. By default, NestJS uses Express as its core HTTP processing library. Key advantages include a tight modular architecture inspired by Angular and strong TypeScript support to reduce code errors.

Controllers serve as the layer for processing incoming requests and returning responses to the client side. Their primary responsibility is to receive HTTP requests, route data to business logic services, and coordinate the returned result in the correct format.

Swagger is a powerful suite of tools used for designing, building, and documenting RESTful APIs developed based on the OpenAPI data format specification. In NestJS, it helps automatically generate an intuitive UI interface for testing endpoints, enabling developers and stakeholders to understand the API structure without directly reading the source code.

Detail

After creating the NestJS project (you can refer back to this tutorial article), create the create-test.dto.ts file with the following content:

import {IsBoolean, IsNotEmpty, IsNumber, IsOptional, IsString} from 'class-validator'

export class CreateTest {
@IsString()
@IsNotEmpty()
name: string

@IsNumber()
@IsOptional()
number?: string

@IsBoolean()
@IsOptional()
checked?: boolean
}

This code defines the CreateTest class as a Data Transfer Object (DTO) to specify data types and perform input data validation. It uses decorators from class-validator to ensure the name property is a required string, while number and checked are optional with corresponding numeric and boolean types.


Next is the test.controller.ts file:

import {
BadRequestException,
Body,
Controller,
Delete,
Get,
Logger,
Param,
ParseIntPipe,
Post,
Put,
Query,
Req,
UploadedFile,
} from '@nestjs/common'
import {FileInterceptor} from '@nestjs/platform-express'
import {ApiBody, ApiConsumes} from '@nestjs/swagger'
import {diskStorage} from 'multer'
import {extname} from 'path'
import {CreateTest} from 'src/dto/create-test.dto'

@Controller('test')
export class TestController {
private readonly logger = new Logger(TestController.name)

@Get('get-text')
async getText() {
this.logger.log('test1')
return 'test-1'
}

@Get('throw-error')
async throwError() {
this.logger.error('test2')
return this.tmpFunc()
}

@Get('query')
async query(@Query('value') value: string) {
return value + value
}

@Get('query-pipe')
async queryPipe(@Query('value', ParseIntPipe) value: number) {
return value + value
}

@Get('param/:value')
async getParam(@Param('value', ParseIntPipe) value: number) {
return value + value
}

@Get('multi-param/:value1/:value2')
async getMultiParam(@Param() params: {value1: string; value2: string}) {
return params.value1 + params.value2
}

@Post('create')
async create(@Body() data: CreateTest) {
return {message: 'Successful', data}
}

@Put('put/:id')
async put(@Param('id', ParseIntPipe) id: number) {
return {message: 'Successful', id}
}

@Delete('delete/:id')
async delete(@Param('id', ParseIntPipe) id: number) {
return {message: 'Successful', id}
}

@Post('upload')
@UseInterceptors(
FileInterceptor('file', {
storage: diskStorage({
destination: './public',
filename: (req, file, callback) => {
const uniqueSuffix =
Date.now() + '-' + Math.round(Math.random() * 1e9)
const ext = extname(file.originalname)
callback(null, `${file.fieldname}-${uniqueSuffix}${ext}`)
},
}),
})
)
@ApiConsumes('multipart/form-data')
@ApiBody({
schema: {
type: 'object',
properties: {
file: {
type: 'string',
format: 'binary',
description: 'Description of file',
},
},
},
})
uploadFile(@UploadedFile() file: Express.Multer.File, @Req() req) {
const protocol = req.protocol
const host = req.get('host')
const url = `${protocol}://${host}/public/${file.filename}`
return {
filename: file.filename,
url,
}
}

tmpFunc() {
this.logger.error('tmpFunc')
throw new BadRequestException('BadRequestException tmpFunc')
}
}

This controller defines endpoints to perform complete HTTP methods (GET, POST, PUT, DELETE). It handles receiving parameters via Query, Param, and Body, while integrating file upload functionality to the public directory and configuring Swagger to display the file upload interface. Beyond standard CRUD APIs, the api with the most configuration is /upload because you must provide information for Swagger to recognize the input information as a file to show on Swagger UI, configure the file storage location, and the filename after upload. Note that this is my guide for using NestJS to upload files; in reality, you should separate the server's logical processing from data storage to another service like AWS S3 for the system to operate efficiently.


Update main.ts file:

import {NestFactory} from '@nestjs/core'
import {AppModule} from './app.module'
import {NestExpressApplication} from '@nestjs/platform-express'
import {join} from 'path'
import {SwaggerModule, DocumentBuilder} from '@nestjs/swagger'

async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule)

app.useStaticAssets(join(process.cwd(), 'public'), {
prefix: '/public',
})

const config = new DocumentBuilder()
.setTitle('NestJS API')
.setDescription('The API description')
.setVersion('1.0')
.addTag('tag name')
.build()
const document = SwaggerModule.createDocument(app, config)
SwaggerModule.setup('api', app, document)

await app.listen(process.env.PORT ?? 3000)
}
bootstrap()

This code performs the initialization of the NestJS application, sets up access rights for static assets in the public directory, and configures SwaggerModule. It sets basic information for the API documentation and defines the access path for the Swagger interface at /api.


Update configuration in nest-cli.json file:

{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true,
"assets": [
"assets/**/*",
{
"include": "public/**/*",
"watchAssets": true
}
],
"plugins": [
{
"name": "@nestjs/swagger",
"options": {
"classValidatorShim": true,
"introspectComments": true,
"dtoFileNameSuffix": [
".dto.ts",
".entity.ts"
]
}
}
]
}
}

This configuration file registers the @nestjs/swagger plugin to automate documentation generation from the source code. It also ensures files in the public directory are managed as application resources and allows Swagger to automatically recognize information from class-validators in DTO/Entity files.


Check the APIs with Postman as follows:











Access Swagger for full API documentation/information





File upload process results



Happy coding!

See more articles here.

Comments

Popular posts from this blog

All Practice Series

Deploying a NodeJS Server on Google Kubernetes Engine

Kubernetes Deployment for Zero Downtime

Setting up Kubernetes Dashboard with Kind

Using Kafka with Docker and NodeJS

Monitoring with cAdvisor, Prometheus and Grafana on Docker

Practicing with Google Cloud Platform - Google Kubernetes Engine to deploy nginx

Kubernetes Practice Series

NodeJS Practice Series

Sitemap