Trying out NestJS part 3: Creating an OpenAPI document
Tags: nestjs, openapi
Published: 2021-03-06 15:31:03


*This article was originally published on my [personal blog](https://arnaudcortisse.com/blog).*

## Introduction

In my [last blog post](https://dev.to/arnaudcortisse/trying-out-nestjs-part-2-creating-rest-endpoints-1cc8), I showed you how easy it is to set up REST API routes using NestJS.

In this blog post, I'd like to try out the OpenAPI module NestJS provides. I'm doing it at this stage because I want to be able to test my routes easily without resorting to any external tools (like Postman).

## Getting Started

The source code for this part of the project is available here: https://github.com/arnaud-cortisse/trying-out-nestjs-part-3.

The official documentation is here: [OpenAPI](https://docs.nestjs.com/openapi/introduction).

- Stop the running Docker containers, if any.
- Go in `packages/nestjs` and run `npm install --save @nestjs/swagger swagger-ui-express`.
- Go in the root directory and type in `docker-compose up --build`.

Now you're good to go with the OpenAPI module.

## Creating the OpenAPI document

The `bootstrap` function (in main.ts) is where you're going to instantiate the OpenAPI module.

```
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('Tasks')
    .setDescription('The tasks API description')
    .setVersion('1.0')
    .addTag('tasks')
    .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(process.env.NEST_PORT);
}
bootstrap();
```

The code speaks for itself: we're essentially setting up the module that's going to deal with the OpenAPI documentation. `SwaggerModule.setup('api', app, document);` makes the documentation available on the route /api.

## Testing Current Setup

Once `main.ts` has been modified, just go over `http://localhost:3001/api/` in the browser. You should have the following:

![openapi_1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ft2rc28odflm6wjdcx1x.png) 

As you can see, the `OpenAPI` module in NestJS is pretty cool: it automatically picks up the existing routes and DTOs and makes them available.

However, you can see that every route is under the tab **default**, while I would have liked the routes related to the **Tasks** to be under the tab **Tasks**. In order to fix that, modify the `tasks.controller.ts` file and add the `@ApiTags('tasks')` decorator:

```
...
import { ApiTags } from '@nestjs/swagger';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
...
```

and reload `http://localhost:3001/api/`.

## Creating the Task DTO

`TaskDto` is going to be a class containing an exhaustive list of all the properties a Task contains.

Let's create a file `task.dto.ts` inside `./packages/nestjs/tasks/dto/`.

```
import { ApiProperty } from '@nestjs/swagger';

export class TaskDto {
  id: number;
  title: string;
  description: string;
}
```

## Modifying CreateTaskDto

`CreateTaskDto` will be used to create new `Tasks`. Right now it's empty (both in code and in the OpenAPI document). Let's change that:

```
import { PickType } from '@nestjs/swagger';
import { TaskDto } from './task.dto';

export class CreateTaskDto extends PickType(TaskDto, [
  'description',
  'title',
] as const) {}

```

You can see that I used the utility class `PickType`. That way, we can keep the code as DRY as possible.

When creating a task, we want to provide a title and a description, while the ID should somehow be generated internally (we don't have any DB yet).

When I refresh `http://localhost:3001/api/`, I can't see any of my changes reflected in the OpenAPI document:

![create-task-dto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sh1gw7u8vo0jwjv7gk2.png)

It's because I didn't explicitly tell the OpenAPI module that I wanted the new properties to be exposed. To do so, just add the decorator `@ApiProperty`:

```
export class TaskDto {
  @ApiProperty()
  id: number;

  @ApiProperty()
  title: string;

  @ApiProperty()
  description: string;
}
```

Now you can see those properties if you go over `http://localhost:3001/api/`:

![create-task-dto-1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7sa0mi6glmn07h72j7t2.png) 

But what's cool about it is that I can now play with the OpenAPI UI more easily.

Say that you want to test the route POST /tasks. Just use the OpenAPI UI (by clicking on _Try it out_):

![post-tasks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpf2g1pj5qlxe7mv7lz5.png) 

The request body will be prefilled with some default values and you'll be able to visualize what is your API replying very easily:

![post-tasks-response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k4kndfsrr153ll7vygy0.png) 

## Modifying UpdateTaskDto

`UpdateTaskDto` will be used to update our tasks. It currently looks like this:

```
import { PartialType } from '@nestjs/mapped-types';
import { CreateTaskDto } from './create-task.dto';

export class UpdateTaskDto extends PartialType(CreateTaskDto) {}
```

We want to be able to update both the _title_ and the _description_, so we don't need the utility class `PartialType`:

```
import { CreateTaskDto } from './create-task.dto';

export class UpdateTaskDto extends CreateTaskDto {}
```

That change is now reflected in the OpenAPI endpoint:

![update-task-dto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8e919bwyxtc7xts1ac32.png) 

![put-tasks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udn1ig7y9nloqi5muqsp.png) 

## Add more information about the input parameters

At present the only pieces of information we have about the _title_ and the _description_ is that there are strings. We could also decide to provide further information, such as the minimum and the maximum length of those strings:

```
export class TaskDto {
  @ApiProperty({
    description: 'The ID of the task',
    default: 'This is a fake ID',
  })
  id: number;

  @ApiProperty({
    description: 'The title of the task',
    minLength: 3,
    maxLength: 30,
    default: 'This is a fake title',
  })
  title: string;
  @ApiProperty({
    description: 'The description of the task',
    minLength: 0,
    maxLength: 200,
    default: 'This is a fake description',
  })
  description: string;
}
```

which translates like this in the OpenAPI endpoint:

![api-property-decorator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/enquif8ct34akisjsxvu.png) 

Be aware that those decorators don't perform any data validation operations. By the way, NestJS recommends the use of the [class-validator](https://docs.nestjs.com/techniques/validation) package for data validation (which I'll try out later).

## Add further information about the responses

Currently our API don't expose what any of its responses look like. The default is status code 200 and doesn't specify the shape of the data. Let's improve that.

In `tasks.controller.ts`, add these lines:

```
...
  @Get(':id')
  @ApiOkResponse({
    description: 'The task has been successfully found.',
    type: TaskDto,
  })
  findOne(@Param('id') id: string) {
    return this.tasksService.findOne(+id);
  }
...
```

The `@ApiOkResponse` decorator allows us to specify what our API responds code-wise (200 in this case, but we could have used the `@ApiResponse` decorator and specified the response code ourselves) as well as the shape of the data returned. It yields the following:

![findone-response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mi82ucfhcm61mr5nc7wh.png) 

You can also specify what the response looks like when you can't find the requested task:

```
...
  @ApiNotFoundResponse({
    description: "Couldn't find the task",
  })
...
```

## Final Words

As you can see, getting started with OpenAPI inside NestJS is very easy. I would strongly encourage people to document their APIs using such tools: It's directly integrated inside the source code and it's very easy to maintain. The benefits of documenting your APIs are multiples. Among other things, that makes it very easy to understand and test them.