Controllers and Routes – Building the API Layer in NestJS

Table of Contents

  1. Introduction to Controllers
  2. Role of Controllers in the Request-Response Cycle
  3. Creating a Controller with @Controller()
  4. HTTP Methods: @Get(), @Post(), @Put(), @Delete(), etc.
  5. Route Parameters and Dynamic Routing
  6. Query Parameters and Request Body Handling
  7. Response Customization with @Res()
  8. Status Codes and Exception Handling in Controllers
  9. Using Services in Controllers via Dependency Injection
  10. Organizing Routes and RESTful Design
  11. Summary and What’s Next

1. Introduction to Controllers

In NestJS, controllers handle incoming HTTP requests and return responses to the client. They are the entry points to your application’s API layer.

A controller is a class annotated with the @Controller() decorator. You define routes and attach them to HTTP method handlers within these classes.


2. Role of Controllers in the Request-Response Cycle

Here’s how a request flows through NestJS:

  1. HTTP request hits a defined route.
  2. Nest routes it to the corresponding controller method.
  3. Controller delegates business logic to a service.
  4. Service performs logic, returns result.
  5. Controller sends response back to the client.

Controllers don’t hold business logic — they delegate it to providers (usually services).


3. Creating a Controller with @Controller()

To create a controller manually:

bashCopyEditnest g controller users

Or by hand:

tsCopyEditimport { Controller } from '@nestjs/common';

@Controller('users')
export class UsersController {}

The string 'users' defines a route prefix. Every method inside will automatically be prefixed with /users.


4. HTTP Methods in NestJS

You use decorators like @Get(), @Post(), etc. to define routes.

tsCopyEdit@Controller('users')
export class UsersController {
  @Get()
  findAll() {
    return 'This action returns all users';
  }

  @Post()
  create() {
    return 'This action creates a user';
  }

  @Put(':id')
  update(@Param('id') id: string) {
    return `This updates user with ID: ${id}`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `User ${id} deleted`;
  }
}

5. Route Parameters and Dynamic Routing

Use @Param() to get dynamic route variables:

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

You can also use a DTO for complex param validation with Pipes (discussed in later modules).


6. Query Parameters and Request Body Handling

  • Query Strings: Use @Query() decorator
tsCopyEdit@Get()
getByQuery(@Query('role') role: string) {
  return `Users with role: ${role}`;
}
  • Request Body: Use @Body()
tsCopyEdit@Post()
create(@Body() createUserDto: CreateUserDto) {
  return this.usersService.create(createUserDto);
}

7. Response Customization with @Res()

Sometimes you need fine-grained control over the response object:

tsCopyEdit@Get()
customResponse(@Res() res: Response) {
  res.status(200).json({ message: 'Hello from NestJS!' });
}

Note: Using @Res() makes the route handler incompatible with interceptors or automatic response transformation.


8. Status Codes and Exception Handling in Controllers

Set custom HTTP status codes using decorators:

tsCopyEdit@Post()
@HttpCode(201)
createUser() {
  return { message: 'User created' };
}

Throw errors using built-in exceptions:

tsCopyEdit@Get(':id')
getUser(@Param('id') id: string) {
  if (!user) {
    throw new NotFoundException(`User ${id} not found`);
  }
  return user;
}

9. Using Services in Controllers via Dependency Injection

A controller shouldn’t contain business logic. Inject a service:

tsCopyEdit@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

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

Nest will inject the corresponding service if it’s declared in the same or imported module.


10. Organizing Routes and RESTful Design

Follow RESTful best practices:

  • GET /users: List all users
  • GET /users/:id: Get a user by ID
  • POST /users: Create a new user
  • PUT /users/:id: Update a user
  • DELETE /users/:id: Remove a user

Structure controllers around features, not HTTP verbs.


11. Summary and What’s Next

In this module, you’ve learned:

  • How to define routes and controllers in NestJS
  • How to handle route and query parameters
  • How to respond to HTTP requests using decorators
  • How to inject services into controllers