NestJS Route Parameters

Route parameters are variable segments inside a URL. They let you pass data directly in the URL path instead of using query strings or request bodies. Route parameters appear in URLs like /users/42 or /orders/99/items/5, where 42, 99, and 5 are dynamic values your controller reads at runtime.

When to Use Route Parameters

Use route parameters to identify a specific resource. A product ID, user ID, article slug — any value that pinpoints one record in a collection belongs in the URL path.

URL Type                    | Use Case
----------------------------|---------------------------
GET /users/42               | Fetch one user by ID
GET /posts/how-to-code      | Fetch post by slug
GET /orders/99/items/5      | Fetch item 5 from order 99
DELETE /files/report.pdf    | Delete a file by name

Defining a Route Parameter

In NestJS, you define a route parameter by prefixing its name with a colon (:) inside the method decorator:

@Get(':id')
findOne(@Param('id') id: string) {
  return `Fetching user with ID: ${id}`;
}

The :id in @Get(':id') declares the parameter. The @Param('id') decorator extracts the value from the URL and passes it into the id argument. A request to GET /users/42 delivers id = '42'.

Route Parameters Are Always Strings

NestJS extracts route parameters as strings. If your logic needs a number (like a database ID), convert it explicitly:

@Get(':id')
findOne(@Param('id') id: string) {
  const numericId = parseInt(id, 10);  // convert string to number
  return this.usersService.findOne(numericId);
}

A shorthand using the unary + operator converts the string inline:

@Get(':id')
findOne(@Param('id') id: string) {
  return this.usersService.findOne(+id);  // +id converts '42' → 42
}

Extracting All Parameters at Once

When a route has several parameters, you can grab them all as an object instead of declaring each one separately:

@Get(':orderId/items/:itemId')
findItem(@Param() params: { orderId: string; itemId: string }) {
  return `Order ${params.orderId}, Item ${params.itemId}`;
}

A request to GET /orders/99/items/5 delivers params = { orderId: '99', itemId: '5' }.

Nested Route Parameters Diagram

URL: /orders/99/items/5

Controller base: @Controller('orders')

  Route: @Get(':orderId/items/:itemId')

    orderId = '99'  ← extracted from the first :orderId segment
    itemId  = '5'   ← extracted from the second :itemId segment

Full matched path: /orders/:orderId/items/:itemId

Optional Parameters

NestJS does not support truly optional URL parameters through the standard @Param() approach. The recommended pattern is to create two separate routes — one with the parameter and one without:

@Get()
findAll() {
  return this.usersService.findAll();
}

@Get(':id')
findOne(@Param('id') id: string) {
  return this.usersService.findOne(+id);
}

NestJS matches routes in the order they appear. Placing @Get()` before `@Get(':id') ensures the more specific route does not accidentally catch requests meant for the general one.

Using Wildcards in Routes

NestJS supports the * wildcard to match any characters in a URL segment:

@Get('ab*cd')
matchAll() {
  // Matches: /abcd, /ab-123cd, /ab-anything-cd
  return 'wildcard matched';
}

Wildcards are useful for catch-all routes, but use them carefully. An overly broad wildcard can accidentally swallow requests meant for other routes.

Route Parameter Validation

NestJS does not automatically validate that :id is a number. If a user calls GET /users/not-a-number, your code receives the string 'not-a-number', and converting it with + produces NaN.

Add a ParseIntPipe to validate and convert in one step:

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
  // id is now guaranteed to be a number
  // NestJS automatically returns 400 Bad Request if id is not a valid integer
  return this.usersService.findOne(id);
}

The ParseIntPipe stops invalid requests before they reach your service and returns a 400 error automatically. Route parameters combined with pipes give you clean, safe URL handling with minimal code.

Leave a Comment

Your email address will not be published. Required fields are marked *