nestjs-rest-querynestjs-rest-query
Guia de UsoGuia de Uso

Guia de Uso

Como usar o nestjs-rest-query em controllers NestJS — decorators, DTOs, service e estrutura da resposta.

Se você ainda não tem um endpoint funcionando, comece por Primeiro endpoint. Esta página é a referência dos decorators, parâmetros, operadores e whitelist de segurança.

Decorators

@ApiDynamicQuery(rules)

Decorator de método para endpoints que precisam de documentação Swagger. Faz duas coisas simultaneamente:

  • Armazena o RulesConfig como metadata no método (lido por @QueryRules() em runtime)
  • Gera @ApiQuery automaticamente para todos os parâmetros suportados (filtros, sort, fields, includes, page, perPage)
@Get()
@ApiDynamicQuery({
  filters: ['name', 'email', 'createdAt'],
  sorts: ['name', 'createdAt'],
  fields: ['id', 'name', 'email', 'createdAt'],
  includes: ['company'],
})
async findAll(/* ... */) {}

@DynamicQuery(rules)

Idêntico ao @ApiDynamicQuery, mas sem geração de Swagger. Use em endpoints internos ou quando não tiver @nestjs/swagger instalado.

@Get()
@DynamicQuery({
  filters: ['name', 'status'],
  sorts: ['name'],
})
async findAll(/* ... */) {}

@QueryRules()

Decorator de parâmetro que lê o RulesConfig armazenado pelo @ApiDynamicQuery ou @DynamicQuery e o injeta como argumento do método em runtime.

async findAll(
  @Query() query: DynamicQueryDto,
  @QueryRules() rules: RulesConfig,  // reads the whitelist from the method decorator
) {}

@QueryRules() depende do metadata gerado por @ApiDynamicQuery ou @DynamicQuery no mesmo método. Sem um desses decorators, rules será {}.

Controller completo

users.controller.ts
import { Controller, Get, Query } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import {
  ApiDynamicQuery,
  ApiPaginatedResponse,
  DynamicQueryDto,
  QueryResult,
  QueryRules,
  RulesConfig,
} from 'nestjs-rest-query';
import { User } from './entities/user.entity';
import { UsersBusiness } from './users.business';

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

  @Get()
  @ApiOperation({ summary: 'Lista usuários com filtros dinâmicos' })
  @ApiDynamicQuery({
    filters: ['username', 'email', 'firstName', 'lastName', 'createdAt'],
    sorts: ['username', 'email', 'createdAt'],
    fields: ['id', 'username', 'email', 'firstName', 'lastName', 'createdAt'],
  })
  @ApiPaginatedResponse(User)
  async findAll(
    @Query() query: DynamicQueryDto,
    @QueryRules() rules: RulesConfig
  ): Promise<QueryResult<User>> {
    return this.usersBusiness.findAll(query, rules);
  }
}

Service

Injete QueryBuilderService e o Repository normalmente via injeção de dependência do NestJS. Como o módulo é @Global, o service está disponível em qualquer provider sem precisar importar DynamicQueryBuilderModule novamente.

users.business.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import {
  DynamicQueryDto,
  QueryBuilderService,
  QueryResult,
  RulesConfig,
} from 'nestjs-rest-query';
import { User } from './entities/user.entity';

@Injectable()
export class UsersBusiness {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private readonly queryBuilderService: QueryBuilderService
  ) {}

  async findAll(
    query: DynamicQueryDto,
    rules: RulesConfig
  ): Promise<QueryResult<User>> {
    return this.queryBuilderService.execute(this.userRepository, query, rules);
  }
}

Parâmetros de query suportados

ParâmetroFormatoExemplo
filterfilter[campo][operador]=valorfilter[email][eq]=joao@email.com
sortCSV de campos (- para desc)sort=-createdAt,name
fieldslista CSVfields=id,name,email
includeslista CSVincludes=company,roles
pagenúmeropage=2
perPagenúmeroperPage=25
paginatetrue / falsepaginate=false (retorna todos os registros)

Operadores de filtro disponíveis

OperadorDescriçãoExemplo
eqIgualfilter[status][eq]=active
neDiferentefilter[status][ne]=inactive
gtMaior quefilter[age][gt]=18
gteMaior ou igualfilter[age][gte]=18
ltMenor quefilter[price][lt]=100
lteMenor ou igualfilter[price][lte]=100
likeContém (case-sensitive)filter[name][like]=João
ilikeContém (case-insensitive, portável)filter[name][ilike]=joao
inDentro de uma lista (CSV)filter[status][in]=active,pending
notInFora de uma lista (CSV)filter[status][notIn]=deleted
betweenEntre dois valores (val1,val2)filter[createdAt][between]=2024-01-01,2024-12-31
isNullNulo (true) ou não nulo (false)filter[deletedAt][isNull]=true

Resposta paginada

execute() retorna Promise<QueryResult<T>>. Quando paginação está ativa (padrão), a resposta inclui os dados de paginação no topo:

{
  "data": [
    { "id": 1, "name": "Ana Lima", "email": "ana@email.com" },
    { "id": 2, "name": "Bruno Costa", "email": "bruno@email.com" }
  ],
  "page": 1,
  "perPage": 10,
  "total": 42,
  "lastPage": 5
}

Quando paginate=false, a resposta contém apenas { "data": [...] }.

RulesConfig — whitelist de segurança

Cada endpoint define sua própria whitelist através do RulesConfig. Campos não listados na whitelist resultam em 400 Bad Request — a biblioteca nunca executa filtros, sorts ou includes não autorizados.

PropriedadeTipoDescrição
filtersstring[]Campos que podem ser usados em filter[campo][op].
sortsstring[]Campos que podem ser usados em sort=campo ou sort=-campo.
fieldsstring[]Campos que podem ser selecionados via fields=. Também restringe os campos de sort (ver abaixo).
includesstring[]Relações TypeORM que podem ser carregadas via includes=.
aliasstringAlias da entidade no QueryBuilder. Padrão: 'root'.

Atenção: quando fields está definido, ele também restringe os campos de sorts. Um campo presente em sorts mas ausente de fields será rejeitado. Para evitar isso, mantenha fields e sorts em sincronia — ou omita fields se não precisar restringir a seleção de colunas.

RulesConfig.operators pode restringir os operadores de filtro aceitos em um endpoint específico:

@ApiDynamicQuery({
  filters: ['name', 'status'],
  operators: { allowed: ['eq', 'ilike'] },
})

Essa configuração do endpoint tem prioridade sobre o DynamicQueryBuilderModule.forRoot({ operators }) global.

Como a whitelist protege o endpoint

Rendering Mermaid diagram...

Quando um campo não autorizado é usado em filter, sort ou includes, a resposta é:

{
  "message": "Filter field(s) not allowed: password. Allowed fields: username, email, firstName, lastName, createdAt",
  "error": "Bad Request",
  "statusCode": 400
}

Próximos passos

Editar esta página no GitHub

On this page