Dynamic Routes

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 9 min

Dynamic Routes refer to routes that do not have a fixed URL path, but rather rely on parameters to determine what data to return.

Overview

Dynamic routing is the foundation of any RESTful API. It allows a single endpoint pattern to serve data for thousands of different records.

In NestJS, dynamic routes are created by defining variables in the route string using the :parameterName syntax.

Key Concepts

  • Path Parameters: The dynamic segment of the URL (e.g., 123 in /users/123).
  • The @Param() Decorator: Used to inject the dynamic values from the URL into the controller method.
  • Nested Dynamic Routes: You can have multiple dynamic segments in a single route.

Code Examples

Standard Dynamic Route

@Controller('users')
export class UsersController {
  
  // The ':id' segment is dynamic
  @Get(':id')
  getUser(@Param('id') id: string) {
    // If request is GET /users/42, 'id' will be "42"
    return `Fetching user ${id}`;
  }
}

Nested Dynamic Segments

You can create complex hierarchical APIs using multiple dynamic segments.

@Controller('companies')
export class CompaniesController {
  
  // Matches /companies/apple/employees/105
  @Get(':companyId/employees/:employeeId')
  getEmployee(
    @Param('companyId') companyId: string,
    @Param('employeeId') employeeId: string,
  ) {
    return `Fetching employee ${employeeId} from company ${companyId}`;
  }
}

The Conflict Problem (Static vs Dynamic)

One of the most common mistakes in API design is route conflicts. Consider this:

@Controller('users')
export class UsersController {
  
  @Get(':id')
  getUserById(@Param('id') id: string) {
    return `User ID: ${id}`;
  }

  // BUG! This will NEVER execute!
  @Get('active')
  getActiveUsers() {
    return 'Active users';
  }
}

If a client requests GET /users/active, Nest evaluates routes top-to-bottom. It sees @Get(':id') first. Since “active” is a valid string, Nest assumes “active” is the id, and executes getUserById("active").

The Solution: Always define static routes before dynamic routes.

@Controller('users')
export class UsersController {
  
  // Static route FIRST
  @Get('active')
  getActiveUsers() {
    return 'Active users';
  }

  // Dynamic route SECOND
  @Get(':id')
  getUserById(@Param('id') id: string) {
    return `User ID: ${id}`;
  }
}

Best Practices

  • Route Ordering: Always order your routes from most specific (static strings) to least specific (dynamic parameters and wildcards).
  • Validation: Dynamic parameters are always strings. Always use ParseIntPipe or ParseUUIDPipe if you expect the dynamic segment to be a number or UUID.