nestjs-rest-querynestjs-rest-query

Customizando a Query

Como usar o parâmetro customize do execute() para adicionar condições extras, e como implementar busca textual.

O método execute() aceita um quarto parâmetro opcional: um callback que recebe o SelectQueryBuilder já com filtros, sorts, includes e fields aplicados, permitindo adicionar cláusulas extras antes da execução.

Regra prática: nos parâmetros públicos da biblioteca (filters, sorts, fields, includes, search) use nomes de propriedades da entidade. No customize, quando você escrever SQL manual em string, pode usar os nomes físicos de coluna do banco.

execute(repo, query, rules, customize?: (qb: SelectQueryBuilder<T>) => void)

Quando usar

Caso de usoAbordagem
Condições internas não expostas ao cliente (soft delete, tenant, status ativo)customize
Busca textual fora do padrão da libcustomize
Filtrar pelo usuário autenticado (userId = :id)customize
JOINs manuais que a lib não cobrecustomize
Filtros que o cliente deve controlar@ApiDynamicQuery com filters
Carregamento de relaçõesincludes no RulesConfig

Exemplo básico

Forçar uma condição que nunca deve ser exposta como filtro público:

@Get()
@ApiDynamicQuery({
  filters: ['name', 'email'],
  sorts: ['name', 'createdAt'],
})
async findAll(
  @Query() query: DynamicQueryDto,
  @QueryRules() rules: RulesConfig,
) {
  return this.queryBuilderService.execute(
    this.usersRepo,
    query,
    rules,
    (qb) => {
      qb.andWhere('root.status = :status', { status: 'active' });
    },
  );
}

A biblioteca agora possui search nativo. Basta declarar os campos pesquisáveis em RulesConfig.search e enviar ?search=termo:

@Get()
@ApiDynamicQuery({
  filters: ['name', 'createdAt'],
  sorts: ['name', 'createdAt'],
  search: ['name', 'email', 'company.name'],
})
async findAll(
  @Query() query: DynamicQueryDto,
  @QueryRules() rules: RulesConfig,
) {
  return this.queryBuilderService.execute(this.usersRepo, query, rules);
}

Uso: GET /users?search=john

Os campos em search são combinados com OR, usando busca textual case-insensitive. Isso também funciona para relações aninhadas como company.name e items.company.cnpj — a lib reaproveita joins de includes quando existirem e cria joins simples quando necessário.

search é pensado para uma caixa de busca rápida. O cliente envia apenas o termo; o backend decide quais campos podem ser pesquisados.

Quando ainda usar customize para busca

Use customize quando a busca precisar fugir do comportamento padrão da lib, por exemplo:

  • pesos diferentes por campo
  • AND entre grupos de termos
  • busca full-text específica do banco
  • regras condicionais por tenant, role ou contexto de autenticação

Quando usar search em vez de filter

Use filter quando o cliente precisa controlar qual campo filtrar e qual operador usar. Use search quando o frontend só precisa de uma caixa de busca rápida e o backend decide quais campos são pesquisáveis:

filter[name][like]=johnsearch=john
Controle do campoclientebackend
Whitelist da libsimsim, via RulesConfig.search
Múltiplos campos simultâneosnão diretamentesim (OR)
Performanceíndice por campopode requerer índice composto

Usando buildQuery para controle total

Prefira buildQuery() quando precisar de controle completo sobre a execução (ex: getManyAndCount, joins complexos):

const qb = this.queryBuilderService.buildQuery(repo, query, rules);

qb.innerJoin('root.company', 'company').andWhere('company.id = :companyId', {
  companyId,
});

const [data, total] = await qb.getManyAndCount();

buildQuery() retorna o SelectQueryBuilder sem executar a query — todos os filtros, sorts, includes e fields já estão aplicados.

Editar esta página no GitHub

On this page