nestjs-rest-querynestjs-rest-query

Integrando com Swagger UI

Como usar @ApiDynamicQuery para gerar documentação OpenAPI automática e testar filtros dinâmicos pelo Swagger UI.

@ApiDynamicQuery é o decorator principal para endpoints que precisam de documentação OpenAPI. Ele faz duas coisas ao mesmo tempo: define a whitelist de segurança do endpoint e gera os @ApiQuery automaticamente para todos os parâmetros suportados pela biblioteca — filtros, sorts, fields, includes, page e perPage.

Pré-requisitos

Para que a integração funcione, você precisa de:

  • @nestjs/swagger instalado e configurado no projeto
  • SwaggerModule inicializado no bootstrap com DocumentBuilder
  • query parser configurado como extended no Express (necessário para filtros aninhados)
  • dqbSwaggerRequestInterceptor registrado nas opções do SwaggerModule.setup (necessário para testar filtros pela UI)
npm install @nestjs/swagger
main.ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
import { dqbSwaggerRequestInterceptor } from 'nestjs-rest-query';

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

  // Required so filter[field][op]=value is parsed as a nested object
  app.set('query parser', 'extended');

  app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
      transformOptions: { enableImplicitConversion: true },
    })
  );

  const document = SwaggerModule.createDocument(
    app,
    new DocumentBuilder().setTitle('Minha API').setVersion('1.0').build()
  );

  SwaggerModule.setup('/', app, document, {
    swaggerOptions: {
      // Required to test nested filters directly in Swagger UI
      requestInterceptor: dqbSwaggerRequestInterceptor(document),
    },
  });

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

bootstrap();

Usando @ApiDynamicQuery no controller

Decore o método com @ApiDynamicQuery passando a whitelist. O decorator cuida do resto — gera os parâmetros OpenAPI e disponibiliza as regras para @QueryRules() em runtime.

users.controller.ts
import { Controller, Get, Query } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import {
  ApiDynamicQuery,
  DynamicQueryDto,
  QueryRules,
  RulesConfig,
} from 'nestjs-rest-query';
import { UsersService } from './users.service';

@ApiTags('users')
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  @ApiDynamicQuery({
    filters: ['name', 'email', 'status', 'createdAt'],
    sorts: ['name', 'createdAt'],
    fields: ['id', 'name', 'email', 'status', 'createdAt'],
    includes: ['company'],
  })
  findAll(@Query() query: DynamicQueryDto, @QueryRules() rules: RulesConfig) {
    return this.usersService.findAll(query, rules);
  }
}

Com isso, o Swagger UI exibe automaticamente todos os parâmetros suportados para aquele endpoint — cada campo de filtro com seus operadores, sorts disponíveis, fields, includes, page e perPage.

Resultado esperado

Parâmetros gerados

Swagger UI com parâmetros dinâmicos

Resposta da query

Swagger UI com resposta da query

Por que o requestInterceptor é necessário

O Swagger UI monta a URL dos parâmetros de forma diferente do formato esperado pela biblioteca. O dqbSwaggerRequestInterceptor intercepta a requisição antes de ela sair e reconstrói os filtros no formato filter[field][op]=value, que o qs (query parser do Express) consegue expandir para objeto aninhado.

Sem ele, os filtros chegam mal formatados ao controller e são ignorados. Para consumo via Postman, frontend ou qualquer cliente HTTP que monte a URL manualmente, o interceptor não é necessário.


Se você não usa Swagger no projeto, substitua @ApiDynamicQuery por @DynamicQuery — o comportamento da whitelist e do @QueryRules() é idêntico, sem gerar nenhum decorator OpenAPI.

Editar esta página no GitHub

On this page