Automating API Documentation in NestJS with Swagger and Custom Decorators

Automating API docs in NestJS using Swagger and custom decorators saves time, ensures consistency, and improves developer experience. Custom decorators add metadata to controllers and methods, generating interactive and accurate documentation effortlessly.

Automating API Documentation in NestJS with Swagger and Custom Decorators

API documentation can be a real pain, especially when you’re knee-deep in development. But fear not, fellow devs! I’ve discovered a game-changing approach to automating API docs in NestJS using Swagger and custom decorators. Trust me, it’s a total lifesaver.

Let’s start with the basics. Swagger is like the superhero of API documentation tools. It generates beautifully interactive docs that make your APIs a breeze to understand and use. And when you combine it with NestJS, magic happens.

First things first, you’ll need to set up Swagger in your NestJS project. It’s pretty straightforward:

npm install @nestjs/swagger swagger-ui-express

Once you’ve got that installed, it’s time to configure Swagger in your main.ts file:

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

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

  const config = new DocumentBuilder()
    .setTitle('My Awesome API')
    .setDescription('The coolest API you'll ever see')
    .setVersion('1.0')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(3000);
}
bootstrap();

Now, here’s where the real fun begins. Custom decorators are like secret weapons for automating your API docs. They let you add metadata to your controllers and methods, which Swagger then uses to generate those slick docs.

Let’s create a custom decorator for handling API responses:

import { applyDecorators } from '@nestjs/common';
import { ApiResponse, ApiResponseOptions } from '@nestjs/swagger';

export function ApiCustomResponse(options: ApiResponseOptions) {
  return applyDecorators(
    ApiResponse({
      ...options,
      description: options.description || 'Successful operation',
    })
  );
}

This decorator wraps the built-in ApiResponse decorator and adds some default values. Now you can use it in your controllers like this:

import { Controller, Get } from '@nestjs/common';
import { ApiCustomResponse } from './decorators/api-custom-response.decorator';

@Controller('users')
export class UsersController {
  @Get()
  @ApiCustomResponse({ status: 200, description: 'Returns all users' })
  getAllUsers() {
    // Your logic here
  }
}

But why stop there? Let’s create a more complex decorator that handles pagination:

import { applyDecorators } from '@nestjs/common';
import { ApiQuery, ApiResponse } from '@nestjs/swagger';

export function ApiPaginatedResponse(model: any) {
  return applyDecorators(
    ApiQuery({ name: 'page', required: false, type: Number }),
    ApiQuery({ name: 'limit', required: false, type: Number }),
    ApiResponse({
      schema: {
        properties: {
          data: {
            type: 'array',
            items: { $ref: `#/components/schemas/${model.name}` },
          },
          meta: {
            type: 'object',
            properties: {
              totalItems: { type: 'number' },
              itemCount: { type: 'number' },
              itemsPerPage: { type: 'number' },
              totalPages: { type: 'number' },
              currentPage: { type: 'number' },
            },
          },
        },
      },
    })
  );
}

Now you can use this decorator on any method that returns paginated results:

@Get()
@ApiPaginatedResponse(User)
async getUsers(@Query() query: PaginationDto): Promise<PaginatedResult<User>> {
  // Your pagination logic here
}

This approach saves you tons of time and keeps your docs consistent across your entire API. Plus, it’s super flexible – you can create custom decorators for all sorts of common patterns in your API.

But here’s a pro tip: don’t go overboard with custom decorators. Keep them simple and focused. If you find yourself creating a decorator that does too many things, it might be time to break it up into smaller, more reusable pieces.

Now, let’s talk about some best practices for using Swagger and custom decorators in NestJS:

  1. Always use descriptive names for your decorators. ApiPaginatedResponse is way more meaningful than something generic like CustomDecorator1.

  2. Group related decorators in a single file. For example, you could have a file called api-decorators.ts that exports all your custom API-related decorators.

  3. Use TypeScript’s type system to your advantage. Make your decorators type-safe to catch errors early.

  4. Don’t forget about error responses! Create custom decorators for common error scenarios to make your API docs even more informative.

  5. Keep your decorators DRY (Don’t Repeat Yourself). If you find yourself copying and pasting the same Swagger decorators over and over, it’s time for a custom decorator.

One thing I’ve learned from experience is that good API documentation can make or break a project. I once worked on a team where the API docs were a mess, and it led to endless back-and-forth between the frontend and backend teams. After we implemented automated docs with Swagger and custom decorators, those issues practically disappeared overnight.

But it’s not just about making life easier for developers. Good API docs can also be a powerful marketing tool. When your API is well-documented and easy to use, developers are more likely to choose your service over a competitor’s.

Let’s look at another example of how custom decorators can simplify your life. Say you have an API that requires authentication for certain endpoints. You could create a decorator like this:

import { applyDecorators } from '@nestjs/common';
import { ApiBearerAuth, ApiUnauthorizedResponse } from '@nestjs/swagger';

export function ApiAuth() {
  return applyDecorators(
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Unauthorized' })
  );
}

Now, instead of adding multiple decorators to every protected endpoint, you can just use @ApiAuth(). It’s cleaner, more maintainable, and less error-prone.

But what about more complex scenarios? Let’s say you have an API that returns different responses based on the user’s role. You could create a decorator that handles this:

import { applyDecorators } from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';

export function ApiRoleBasedResponse(options: {
  adminResponse: any;
  userResponse: any;
}) {
  return applyDecorators(
    ApiResponse({
      schema: {
        oneOf: [
          { $ref: `#/components/schemas/${options.adminResponse.name}` },
          { $ref: `#/components/schemas/${options.userResponse.name}` },
        ],
      },
    })
  );
}

This decorator allows you to specify different response schemas based on the user’s role, making your API docs more accurate and useful.

Remember, the goal here is to make your life easier while also improving the quality of your API documentation. It’s a win-win situation!

One last tip: don’t forget to update your custom decorators as your API evolves. It’s easy to create a decorator and then forget about it, but keeping them up-to-date is crucial for maintaining accurate documentation.

In conclusion, automating API documentation in NestJS with Swagger and custom decorators is a powerful technique that can save you time, reduce errors, and make your APIs more developer-friendly. By creating reusable, type-safe decorators, you can ensure consistency across your API docs and make it easier for other developers (including future you) to understand and use your API.

So go forth and document those APIs! Your future self (and your fellow developers) will thank you.