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/swaggerinstalado e configurado no projetoSwaggerModuleinicializado nobootstrapcomDocumentBuilderquery parserconfigurado comoextendedno Express (necessário para filtros aninhados)dqbSwaggerRequestInterceptorregistrado nas opções doSwaggerModule.setup(necessário para testar filtros pela UI)
npm install @nestjs/swaggerimport { 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.
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

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.

