Integrating Swagger with a NestJS Application
by Anukritigarg, Apurva Jain, Swetha Gumpena
What is NestJS?
NestJS seems to be the new buzz framework among backend developers who love to write their code in Typescript. The framework also enables to develop your server with pure Javascript. It provides a higher-level abstraction for your express/fastify HTTP server frameworks and brings forth an AngularJS like structure to work with. It can be used to build “efficient, reliable and scalable” NodeJs servers.
What is Swagger and why we use it?
Swagger has been the go-to for documenting the REST API’s and serving as a medium of understanding and experimentation among developers. It is best to document your API, since it gives a look through into your API structure, the endpoints it exposes and the request/response structure. The best part about swagger being, the documentation can be automatically generated so that you don’t spend your weekends documenting what you build during weekdays :)
In this tutorial, we’ll take a step-by-step look at how you can document your NestJS application using Swagger.
Before diving further, let’s get a simple Nest application set up on your local so that you can follow along. It uses typescript at its core.
https://github.com/anukritigarg13/nest-example-e-commerce
Clone the above repository in your local and follow the steps below to get your Nest application running locally:
Step 1
$ npm install
Step 2 (starts your server in development mode)
$ npm run start:dev
And voila! The Nest server is running at localhost:8080 and you can hit the url in your browser or postman to be greeted warmly:)
All things setup, now let’s move onto building your basic swagger:
Step 1: Run
$ npm install --save @nestjs/swagger swagger-ui-express
Step 2: In the project’s main.ts file add the following import
import { SwaggerModule, DocumentBuilder } from ‘@nestjs/swagger’;
import { ProductsModule } from './modules/products/products.module';
Step 3: Add this code snippet inside the bootstrap function. This basically initialises your swagger documentation and you can access it at localhost:8080/api.
const config = new DocumentBuilder()
.setTitle('E-commerce') //the title you want for your swagger docs
.setDescription('E-commerce API description') //description
.setVersion('1.0') //version setting for the docs
.build();const document = SwaggerModule.createDocument(app, config, {
include: [ProductsModule], //the modules that you want to include in your swagger docs
});SwaggerModule.setup('api', app, document); // if you want your docs to open up at a different endpoint, you can replace 'api' with endpoint of your choice
You should now be able to see a screen like this
Seems like magic right? You don’t have to take care of writing the .yml/.json file. It is automatically generated and can be downloaded as well. The document
generated through SwaggerModule.createDocument
is a serialisable object that conforms to OpenAPI specifications and instead of hosting, we can download it to use in various other ways.
To take a look and download the swagger .json file, navigate to localhost:8080/api-json
, you’ll be able to see the entire json file in your browser.
When navigating through swagger docs, you’ll notice that the CategoryProductsDto has not been populated and the body and response do not have an example or even any meta info. To make our swagger documentation provide complete information, there are certain decorators provided that we’ll be learning about further. Following are the decorators that have been covered in this article
- ApiProperty
- ApiPropertyOptional
- ApiTags
- ApiOperation
- ApiResponse
- ApiBody
- ApiQuery
- ApiParam
Now let’s take a look at these decorators one by one and integrate them in our project.
- Use the
@ApiProperty()
decorator above each property in your schema/dto that is required in the object and@ApiPropertyOptional()
for a property that’s optional. Both take in an object as an argument wherein we can define various other properties. For e.g.
enum
: If our property has a type that is an enum, we can provide this in our decorator as well to indicate swagger of the options available in enumexample
: If we want to show any example for our property in the swagger UIdefault
: To let swagger know of any default value of the propertytype
: If the type of our property is an array of custom schema,@ApiProperty({ type: () =>[CategoryProductsDto] })
, using the decorator like this, makes the swagger schema take into account this situation
Examples:
@ApiProperty({ enum: STATUS, example: STATUS.AVAILABLE })
status: STATUS;@ApiPropertyOptional({
enum: CATEGORY,
example: CATEGORY.STATIONERY,
default: CATEGORY.STATIONERY
})
category?: CATEGORY;
A lot of theory up till now, let’s give you a small task. Try to add the above decorators for all your schemas. To take a look at how I did it, checkout to the feat/swagger-integration
branch of my repository and navigate to products.dto.ts file.
2. Use the @ApiTags()
decorator for your controller. It is used to provide a distinct tag. for your controller. In our example, currently you’ll see a default tag being given to the products endpoints. To change it into a tag of your choice, use this decorator. For e.g., adding this tag
@ApiTags('products')@Controller('products')
export class ProductsController{......}
will give you a visual like this, instead of the default.
3. Use @ApiOperation()
over your methods in controller to provide a summary and description about what this endpoint does. It takes an object as a parameter. For e.g. I added this decorator to provide information about my (GET) /products endpoint
@ApiOperation({
summary: 'Gets all products from every category',
description:'If you want to get all the products from your inventory and from each category, use this route. It takes no path or query params'
})@Get()
getAllProducts(): GetAllProductsDto {
return this.productsService.getAllProducts();
}
This will give you an endpoint that looks like this, more informative no?
4. Use @ApiResponse()
decorator above your method to define the response structure for your endpoint. Currently in the swagger UI, we are not aware of what response structure does our endpoint hold. This decorator also takes in an object as an argument. In the type
we can provide the dto/schema object of our response. For e.g. adding a decorator like this,
@ApiResponse({
type: GetAllProductsDto,
status: 200,
description: 'All the products received'
})
populates my UI in the following manner,
Pssst, looks a little different? I added examples to my ApiProperty decorators 😬
5. Use @ApiBody()
decorator above your method to define the structure of what your request body should look like for the endpoint. For e.g. adding this decorator to my post route
@ApiBody({
type: CategoryProductsDto,
description: 'Store product structure',
})@Post()
postCategoryProduct(@Body() details: CategoryProductsDto ):CategoryProductsDto {
return this.productsService.postCategoryProduct(details);
}
helps me in conveying what all my route requires in request body
6. Similar to the @ApiBody()
decorator, use the @ApiQuery()
and @ApiParam()
to define the structure of query and path params of the endpoint. These decorators also take in an object as argument. For. e.g.
@ApiParam({ name: 'category', type: 'string', enum: CATEGORY })@ApiQuery({ name: 'status', type: 'string', enum: STATUS, required: false }) //this query isn't required, hence added a false@Get('/:category')getCategoryProduct(
@Param('category') category: CATEGORY, @Query('status') status: STATUS): CategoryProductsDto[] {
return this.productsService.getCategoryProducts(category, status);
}
Conclusion
That’s all ✨ Now that you have all the tools to build up the swagger documentation, try to practice on the repository and get hands-on experience. You can find the whole solution by checking out to feat/swagger-integration branch. Hope this article was helpful and will save you time while documenting your next API 😄
If you liked this post, please share, comment and give few 👏 😊