NestJS Query Strings
Query strings are key-value pairs appended to a URL after a question mark. They carry optional filters, search terms, sorting instructions, and pagination settings. Unlike route parameters that identify a resource, query strings shape how a collection of resources is returned.
What a Query String Looks Like
/users?role=admin&active=true&page=2&limit=10 Breaking it down: ? ← marks the start of the query string role=admin ← key: role, value: admin & ← separator between key-value pairs active=true page=2 limit=10
Reading a Single Query Parameter
Use the @Query() decorator with a key name to extract one value:
@Get()
findAll(@Query('role') role: string) {
return `Filtering users with role: ${role}`;
}
A request to GET /users?role=admin delivers role = 'admin'. If the query parameter is missing from the URL, role is undefined.
Reading All Query Parameters at Once
Omit the key name to receive the entire query string as an object:
@Get()
findAll(@Query() query: Record<string, string>) {
console.log(query);
// { role: 'admin', active: 'true', page: '2', limit: '10' }
return query;
}
Using a DTO for Query Parameters
When a route accepts several query parameters, a DTO (Data Transfer Object) keeps the code organized and type-safe:
// find-users-query.dto.ts
export class FindUsersQueryDto {
role?: string;
active?: string;
page?: string;
limit?: string;
}
// Controller
@Get()
findAll(@Query() query: FindUsersQueryDto) {
const page = parseInt(query.page || '1', 10);
const limit = parseInt(query.limit || '10', 10);
return this.usersService.findAll({ role: query.role, page, limit });
}
Query Strings vs Route Parameters — When to Use Each
Situation | Use
------------------------------------|------------------
Identifying one resource | Route parameter
/users/42 | @Param('id')
Filtering a collection | Query string
/users?role=admin | @Query('role')
Sorting results | Query string
/products?sort=price&order=asc | @Query()
Pagination | Query string
/posts?page=3&limit=20 | @Query()
Searching by keyword | Query string
/articles?search=nestjs | @Query('search')
Pagination Pattern
Pagination is one of the most common uses of query strings. Here is a clean controller pattern:
Request: GET /products?page=2&limit=5
Page 1: items 1–5
Page 2: items 6–10 ← current request
Page 3: items 11–15
@Get()
findAll(
@Query('page') page = '1',
@Query('limit') limit = '10',
) {
const pageNumber = parseInt(page, 10);
const limitNumber = parseInt(limit, 10);
const skip = (pageNumber - 1) * limitNumber;
return this.productsService.findAll({ skip, take: limitNumber });
}
Default values in the method signature ensure the endpoint works even when page and limit are not provided in the URL.
Query Parameters Are Always Strings
Like route parameters, query string values arrive as strings. The number 2 in ?page=2 arrives as the string '2'. Always convert to the expected type before using the value in logic.
Use ParseIntPipe to handle conversion and validation automatically:
@Get()
findAll(
@Query('page', new ParseIntPipe({ optional: true })) page = 1,
@Query('limit', new ParseIntPipe({ optional: true })) limit = 10,
) {
return this.productsService.findAll({ page, limit });
}
Multiple Values for the Same Key
A query string can repeat the same key to pass an array of values:
GET /products?category=shoes&category=bags&category=hats
@Query('category') category: string | string[]
// delivers: ['shoes', 'bags', 'hats']
NestJS automatically collects repeated keys into an array. Check whether the value is a string or an array if the parameter may appear once or multiple times.
Encoding Special Characters
Query string values with spaces or special characters must be URL-encoded. A space becomes %20 or +. The browser and HTTP clients handle encoding automatically when you build URLs programmatically. NestJS decodes the values before passing them to your controller, so you always receive clean, readable strings in your methods.
Query strings give your API flexibility without complicating your URL design. Use them for anything optional — filtering, sorting, searching, and pagination — while reserving route parameters for required resource identifiers.
