Skip to content

NestJS Routing

Routing determines which controller method handles a given request. This lesson covers combining controller prefixes with route decorators, path patterns, wildcards, and route ordering.

How Routes Are Defined

A route is formed by combining a controller's base path (from @Controller()) with a method's route decorator path. Nest registers these routes with the underlying HTTP adapter (Express or Fastify) in the order the classes and methods are declared.

@Controller('cats')
export class CatsController {
  @Get()
  findAll() { return 'all cats'; }

  @Get('breeds')
  findBreeds() { return 'all breeds'; }

  @Get(':id')
  findOne() { return 'one cat'; }
}

GET /cats/breeds must be registered before GET /cats/:id, otherwise the parameterized route would match /cats/breeds first, treating "breeds" as an :id value.

Route Path Patterns

@Get()            // matches exactly the controller's base path
@Get(':id')       // matches a required path parameter
@Get(':id?')      // matches an optional path parameter
@Get('a*z')       // wildcard pattern matching
@Get('*')         // catch-all within this controller
  • Route parameters are declared with a leading colon, like :id.
  • Wildcards (*) match any characters in that path segment.
  • More specific literal routes should be registered before similarly-shaped parameterized routes.
  • A controller-level prefix combines with each method's path to form the full route.

Routing Cheatsheet

Route decorators and patterns used throughout NestJS controllers.

Pattern Example Matches
Base route @Get() GET /cats
Subpath @Get('active') GET /cats/active
Parameter @Get(':id') GET /cats/42
Multiple parameters @Get(':id/owners/:ownerId') GET /cats/42/owners/7
Any method @All() Any HTTP verb on this route
Redirect @Redirect(url, code) Sends an HTTP redirect response

Route Prefixes and Global Prefixes

Beyond per-controller prefixes, you can set a global prefix for the entire application (e.g. /api) using app.setGlobalPrefix() in main.ts, useful when the API and a separate frontend share the same domain.

const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
await app.listen(3000);
// GET /cats becomes GET /api/cats

Route Ordering Caveats

Just like Express, NestJS matches routes in the order they are registered, which for controllers means the order of methods within a class, and the order controllers are listed within a module.

Common Mistakes

  • Registering a parameterized route before a literal one that could otherwise be shadowed.
  • Forgetting a leading colon on a route parameter, writing @Get('id') instead of @Get(':id').
  • Assuming a global prefix applies retroactively to already-hardcoded absolute URLs in client code.
  • Overusing wildcard routes when a couple of explicit routes would be clearer.

Key Takeaways

  • A full route is the controller's base path combined with the method's route decorator path.
  • Route parameters use a leading colon, and can be optional with a trailing ?.
  • Literal routes should be registered before conflicting parameterized routes.
  • app.setGlobalPrefix() adds a prefix to every route in the application.

Pro Tip

When a controller has both a literal subpath and a parameterized one that could collide (like /cats/featured and /cats/:id), add a short comment above the literal route explaining why its ordering matters, future edits are the most common way this kind of bug gets reintroduced.