Skip to content

NestJS Query Parameters

Query parameters are the standard way to support filtering, pagination, and sorting on list endpoints. This lesson covers @Query() and how to validate query parameters with a dedicated DTO.

Reading Query Parameters

Query parameters appear after a ? in the URL, like /cats?limit=10&page=2. The @Query() decorator extracts them individually by name, or as a single object containing every query parameter.

import { Controller, Get, Query } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  findAll(@Query('limit') limit: string, @Query('page') page: string) {
    return `limit=${limit}, page=${page}`;
  }
}

Just like route parameters, individual query parameters always arrive as strings and must be converted explicitly for numeric comparisons or database queries.

Query Parameters With a DTO

class FindCatsQueryDto {
  limit?: number;
  page?: number;
  breed?: string;
}

@Get()
findAll(@Query() query: FindCatsQueryDto) {
  return query;
}
  • @Query('name') extracts a single query parameter as a string.
  • @Query() with no argument returns an object of every query parameter.
  • Pairing @Query() with a DTO and ValidationPipe validates and transforms query parameters together.
  • Query parameters are always optional from the framework's perspective, mark required ones explicitly in your DTO's validation rules.

Query Parameters Cheatsheet

Common patterns for reading and validating query strings.

Usage Result
@Query('limit') limit: string A single query parameter
@Query() query: any All query parameters as one object
@Query('limit', ParseIntPipe) limit: number Query parameter parsed as a number
@Query() dto: FindCatsQueryDto Validated query object using a DTO class

A Realistic Pagination Example

Combining a query DTO with class-validator decorators and class-transformer lets you validate and convert pagination parameters in one pass, rejecting invalid input before it ever reaches your service layer.

import { Type } from 'class-transformer';
import { IsInt, IsOptional, Min } from 'class-validator';

export class PaginationQueryDto {
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  page: number = 1;

  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  limit: number = 20;
}

@Type(() => Number) (from class-transformer) converts the incoming string query value to a number before class-validator checks it with @IsInt().

Array and Boolean Query Parameters

Query strings don't have a native array or boolean type, both Express and Nest use conventions, repeated keys (?tag=a&tag=b) become an array, and string values like "true"/"false" need explicit conversion with ParseBoolPipe or a transform decorator.

Common Mistakes

  • Comparing a numeric-looking query string directly to a number without converting it first.
  • Not providing sensible defaults for optional pagination parameters like page and limit.
  • Allowing unbounded limit values, letting a client request an enormous page size.
  • Forgetting that repeated query keys need explicit array handling, not just a single string.

Key Takeaways

  • @Query() extracts query string parameters individually or as a full object.
  • Query parameters always arrive as strings and often need explicit conversion.
  • A dedicated query DTO combined with ValidationPipe validates and transforms parameters together.
  • Always cap and default pagination-related query parameters to avoid abuse.

Pro Tip

Always set a maximum allowed limit in your pagination DTO validation (e.g. @Max(100)), otherwise a client can request ?limit=1000000 and put unexpected load on your database.