Controllers are where your application starts to feel “real”. They handle incoming requests and return responses to the client. In NestJS, controllers are clean, structured, and heavily inspired by Angular's architecture.
What Are Controllers?
A controller is responsible for:
- Handling incoming HTTP requests
- Processing input (params, body, query)
- Returning responses (JSON, status codes, etc.)
A controller handles incoming HTTP requests and returns responses to the client. It acts as the entry point of the application and delegates business logic to services.
Imagine you are a pizza place. You tell the waiter: "I would like a large pepperoni pizza."
The waiter:
- Listens to your order
- Understands what you want
- Tells the kitchen to make it
- Brings you the pizza
In NestJS, controllers do exactly this for HTTP requests.
// Real-world analogy in code:
@Controller('pizza') // The "Pizza Department" of your restaurant
export class PizzaController {
@Get('pepperoni') // When someone asks for pepperoni pizza
getPepperoniPizza() {
return 'Here is your delicious pepperoni pizza! 🍕';
}
}Your First Controller: Hello World
Using Nest CLI:
nest generate controller usersThis creates:
users/
users.controller.ts
users.controller.specs.tsBasic example:
import { Controller, Get } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
findAll() {
return 'This returns all users';
}
}Explanation:
@Controller('users'): sets route prefix/users@Get(): handles GET requestsfindAll(): method executed when route is hit
What does @Controller() decorator do?
It defines a class as a controller and sets a route prefix for all endpoints inside it.
Example:
@Controller('users')All routes will start with /users
Let's create the simplest possible controller together:
// app.controller.ts
import { Controller, Get } from '@nestjs/common';
@Controller() // This makes it a controller!
export class AppController {
@Get() // Handles GET requests to the homepage
sayHello(): string {
return 'Hello, World! Welcome to my NestJS app!';
}
@Get('about') // Handles GET requests to /about
getAboutPage(): string {
return 'This is my amazing app!';
}
}What just happened?
@Controller(): Makes this class a controller@Get(): Handles GET requests@Get('about'): Handles GET requests to/about
Try it, if you run this and visit:
http://localhost:3000: You will see “Hello, World!”http://localhost:3000/about: You will see “This is my amazing app!”
Route Paths: The Address System
Controllers can handle requests to different “addresses” (URLs):
@Controller('users') // All routes here start with /users
export class UsersController {
@Get() // GET /users
getAllUsers() { /* ... */ }
@Get('profile') // GET /users/profile
getUserProfile() { /* ... */ }
@Get(':id') // GET /users/123 (dynamic!)
getUserById() { /* ... */ }
}HTTP Methods: Different Types of Requests
Controllers handle different request types:
@Controller('tasks')
export class TasksController {
@Get() // Get all tasks (READ)
getTasks() { return 'All tasks'; }
@Post() // Create a task (CREATE)
createTask() { return 'Task created'; }
@Put(':id') // Update a task (UPDATE)
updateTask() { return 'Task updated'; }
@Delete(':id') // Delete a task (DELETE)
deleteTask() { return 'Task deleted'; }
@Patch(':id') // Partially update a task
partialUpdate() { return 'Task partially updated'; }
}These correspond to CRUD operations:
- Create ->
@Post() - Read ->
@Get() - Update ->
@Put()or@Patch() - Delete ->
@Delete()
How Controllers Receive Information
When someone sends data to your app (like filling out a form), controllers need to receive it. Here are all the ways:
1 Route Parameters: Data in the URL
@Get('users/:id') // The :id is a route parameter
getUser(@Param('id') userId: string) {
return `Fetching user with ID: ${userId}`;
}
// Example: GET /users/123
// Output: "Fetching user with ID: 123"2 Query Parameters: Filters and Options
@Get('products')
getProducts(
@Query('page') page: number,
@Query('limit') limit: number,
@Query('category') category?: string
) {
return `Page: ${page}, Limit: ${limit}, Category: ${category || 'all'}`;
}
// Example: GET /products?page=1&limit=10&category=books
// Output: "Page: 1, Limit: 10, Category: books"3 Request Body: The Main Data
@Post('users')
createUser(@Body() userData: any) {
return `Creating user: ${JSON.stringify(userData)}`;
}
// Example POST to /users with body: {"name": "John", "email": "john@example.com"}
// Output: "Creating user: {"name":"John","email":"john@example.com"}"4 Headers: Metadata About the Request
@Get('secure-data')
getSecureData(@Headers('authorization') authToken: string) {
if (authToken === 'secret-token') {
return 'Here is your secure data!';
}
return 'Access denied!';
}5 Everything Together: The Complete Picture
@Put('products/:id')
updateProduct(
@Param('id') productId: string,
@Body() updateData: any,
@Query('version') version: string
) {
return {
message: `Updating product ${productId}`,
data: updateData,
apiVersion: version
};
}Sending Responses: How Controllers Reply
Controllers don't just receive data – they send it back too.
1 Basic Responses
@Get()
getMessage() {
return 'Hello World'; // Automatic 200 status code
}
@Get('not-found')
getNotFound() {
throw new NotFoundException('Item not found'); // 404 error
}
@Post('created')
createItem() {
// Return 201 Created with location header
return new CreatedResponse('Item created', { id: 1 });
}2 Status Codes
import { HttpCode } from '@nestjs/common';
@Post('custom-status')
@HttpCode(201) // Set custom status code
createWithCustomStatus() {
return { message: 'Created successfully' };
}
@Get('no-content')
@HttpCode(204) // No Content
deleteItem() {
// No response body for 204
}3 Headers in Responses
import { Header } from '@nestjs/common';
@Get('download')
@Header('Content-Type', 'application/pdf')
@Header('Content-Disposition', 'attachment; filename="report.pdf"')
downloadFile() {
// Return file content
return Buffer.from('PDF content here');
}Interview Questions
What is the role of decorators in controllers?
Decorators define metadata and behavior, such as:
- Routing (
@Get) - Parameter extraction (
@Body,@Param) - Response handling
What happens if two routes conflict?
NestJS resolves routes based on:
- Order of declaration
- Specificity (static routes before dynamic ones)
Can a controller have multiple routes prefixes?
Not directly. But you can:
- Use multiple controllers
- Or version routes (
/v1/users,/v2/users)
What are route wildcards?
They allow flexible matching:
@Get('ab*cd')Matches:
/abcd/ab123cd
How do you handle errors inside controllers?
Throw build-in exceptions:
throw new NotFoundException();- Or use custom exception filters
Leave a comment
Your email address will not be published. Required fields are marked *


