> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Controllers & Routing

# Chapter 3: Controllers & Routing

> Controllers are the entry point for handling client requests in NestJS. Think of them as the "reception desk" of your application—they receive requests, ask the right service for help, and send back a response. This chapter explores routing, request/response handling, decorators, middleware, guards, interceptors, error handling, and best practices for building robust APIs.

***

## 3.1 What is a Controller?

A controller is responsible for receiving incoming requests, delegating work to services, and returning responses. Controllers define the routes and HTTP methods for your API.

### Controller Responsibilities

Controllers handle HTTP-specific concerns:

* **Route definition**: Map URLs to handler methods
* **Request extraction**: Get data from params, query, body, headers
* **Response formatting**: Return data in the correct format
* **Error handling**: Throw appropriate HTTP exceptions
* **Validation**: Ensure incoming data is valid (via DTOs and pipes)

**What Controllers Should NOT Do:**

* Business logic (delegate to services)
* Database access (delegate to repositories/services)
* Complex data transformation (delegate to services)
* Authentication logic (use guards)

**Analogy:**

> Think of a controller as a call center operator. When a customer calls (HTTP request arrives), the operator does not fix the product themselves. They listen to the request, look up the customer's account (extract params, query, body), route the call to the right department (delegate to a service), and then relay the department's answer back to the customer (send the response). If the operator starts doing repair work on the phone, the whole system breaks down. That is exactly what happens when you put business logic in controllers -- it becomes untestable, unreusable, and tangled with HTTP concerns.

### Basic Controller Structure

```typescript theme={null}
import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service';

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

  @Get()  // GET /users
  findAll() {
    return this.usersService.findAll();
  }
}
```

**Key Components:**

* `@Controller('users')` - Defines the base route for all methods
* Constructor injection - Services are injected via constructor
* Route decorators - `@Get()`, `@Post()`, etc. define HTTP methods
* Handler methods - Process requests and return responses

**Diagram: Request Flow**

```text theme={null}
HTTP Request → Controller → Service → Repository/Database → Response
```

***

## 3.2 Routing in NestJS

Routes are defined using decorators like `@Get`, `@Post`, `@Put`, `@Delete`, `@Patch`, etc. The path can include parameters, query strings, and wildcards.

### HTTP Method Decorators

NestJS provides decorators for all HTTP methods:

```typescript theme={null}
@Controller('users')
export class UsersController {
  @Get()           // GET /users
  findAll() { }

  @Get(':id')       // GET /users/:id
  findOne() { }

  @Post()           // POST /users
  create() { }

  @Put(':id')       // PUT /users/:id
  update() { }

  @Patch(':id')     // PATCH /users/:id
  partialUpdate() { }

  @Delete(':id')     // DELETE /users/:id
  remove() { }
}
```

### Route Parameters

Extract dynamic segments from the URL:

```typescript theme={null}
@Get(':id')
findOne(@Param('id') id: string) {
  return this.usersService.findOne(Number(id));
}
```

**Multiple Parameters:**

```typescript theme={null}
@Get(':userId/posts/:postId')
findUserPost(
  @Param('userId') userId: string,
  @Param('postId') postId: string,
) {
  return this.postsService.findOne(userId, postId);
}
```

**All Parameters:**

```typescript theme={null}
@Get(':id')
findOne(@Param() params: { id: string }) {
  return this.usersService.findOne(Number(params.id));
}
```

**With Pipes for Validation:**

```typescript theme={null}
import { ParseIntPipe } from '@nestjs/common';

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
  // id is already a number, no need to convert
  return this.usersService.findOne(id);
}
```

### Query Parameters

Extract data from the query string:

```typescript theme={null}
@Get()
findByQuery(@Query('role') role: string) {
  return this.usersService.findByRole(role);
}
```

**Multiple Query Parameters:**

```typescript theme={null}
@Get()
findAll(
  @Query('page') page: number,
  @Query('limit') limit: number,
  @Query('sort') sort: string,
) {
  return this.usersService.findAll({ page, limit, sort });
}
```

**All Query Parameters:**

```typescript theme={null}
@Get()
findAll(@Query() query: { page?: number; limit?: number; sort?: string }) {
  return this.usersService.findAll(query);
}
```

**With DTO for Validation:**

```typescript theme={null}
export class QueryDto {
  @IsOptional()
  @IsInt()
  @Min(1)
  page?: number;

  @IsOptional()
  @IsInt()
  @Min(1)
  @Max(100)
  limit?: number;
}

@Get()
findAll(@Query() query: QueryDto) {
  return this.usersService.findAll(query);
}
```

### Request Body

Extract data from the request body:

```typescript theme={null}
@Post()
create(@Body() createDto: CreateUserDto) {
  return this.usersService.create(createDto);
}
```

**Partial Body:**

```typescript theme={null}
@Post()
create(@Body('name') name: string) {
  // Only extract 'name' from body
  return this.usersService.create({ name });
}
```

**Multiple Body Properties:**

```typescript theme={null}
@Post()
create(
  @Body('name') name: string,
  @Body('email') email: string,
) {
  return this.usersService.create({ name, email });
}
```

### Headers

Extract data from request headers:

```typescript theme={null}
@Get()
findAll(@Headers('authorization') auth: string) {
  return this.usersService.findAll();
}
```

**All Headers:**

```typescript theme={null}
@Get()
findAll(@Headers() headers: Record<string, string>) {
  const auth = headers['authorization'];
  return this.usersService.findAll();
}
```

### Request Object

Access the full request object when needed:

```typescript theme={null}
import { Request } from 'express';

@Get()
findAll(@Req() req: Request) {
  // Access full request object
  return this.usersService.findAll();
}
```

### Response Object

Control the response directly:

```typescript theme={null}
import { Response } from 'express';

@Get()
findAll(@Res() res: Response) {
  return res.status(200).json({ data: [] });
}
```

**Warning:** Using `@Res()` bypasses NestJS's built-in response handling entirely. This means interceptors will not transform your response, exception filters may not catch errors correctly, and NestJS cannot automatically serialize your return value to JSON. Only use `@Res()` when you need low-level control (like streaming files or setting custom cookies). For everything else, return data from the handler and let NestJS serialize it.

### Status Codes

Set custom HTTP status codes:

```typescript theme={null}
import { HttpCode } from '@nestjs/common';

@Post()
@HttpCode(201)  // Returns 201 Created instead of 200
create(@Body() createDto: CreateUserDto) {
  return this.usersService.create(createDto);
}
```

**Common Status Codes:**

```typescript theme={null}
@Post()
@HttpCode(HttpStatus.CREATED)  // 201
create() { }

@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)  // 204
remove() { }
```

### Route Wildcards

Use wildcards for flexible routing:

```typescript theme={null}
@Get('ab*cd')  // Matches abcd, ab_cd, ab123cd, etc.
findWildcard() {
  return 'This route uses a wildcard';
}
```

***

## 3.3 Request Lifecycle

Understanding the request lifecycle helps you know where to place your logic and how different components interact.

### Complete Request Lifecycle

This is one of the most important diagrams in the entire course. Memorize this order, because when something goes wrong in production, knowing exactly where in this pipeline your code runs is the difference between a 5-minute fix and a 5-hour debugging session.

1. **Incoming Request** - HTTP request arrives at the server
2. **Middleware** - Global and route-specific middleware runs (think Express middleware: logging, CORS, body parsing)
3. **Guards** - Authentication and authorization checks ("Should this request proceed at all?")
4. **Interceptors (Before)** - Pre-processing (logging, request timing, cache lookup)
5. **Pipes** - Validation and transformation of input data (DTOs validated here)
6. **Controller** - Route handler method executes (thin -- just delegates to service)
7. **Service** - Business logic executes (the real work happens here)
8. **Interceptors (After)** - Post-processing (response wrapping, cache storage)
9. **Exception Filters** - Handle any exceptions thrown during the pipeline (format error responses)
10. **Response** - HTTP response sent to client

**Diagram: Full Request Lifecycle**

```text theme={null}
HTTP Request
    ↓
Global Middleware
    ↓
Route Middleware
    ↓
Guards (Authentication/Authorization)
    ↓
Interceptors (Before)
    ↓
Pipes (Validation/Transformation)
    ↓
Controller Handler
    ↓
Service (Business Logic)
    ↓
Interceptors (After - Response Transformation)
    ↓
Exception Filters (if error)
    ↓
HTTP Response
```

### Execution Order Example

```typescript theme={null}
// 1. Middleware
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    console.log('1. Middleware');
    next();
  }
}

// 2. Guard
@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    console.log('2. Guard');
    return true;
  }
}

// 3. Interceptor (Before)
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    console.log('3. Interceptor (Before)');
    return next.handle();
  }
}

// 4. Pipe
export class ValidationPipe implements PipeTransform {
  transform(value: any) {
    console.log('4. Pipe');
    return value;
  }
}

// 5. Controller
@Controller('users')
@UseGuards(AuthGuard)
@UseInterceptors(LoggingInterceptor)
export class UsersController {
  @Get()
  @UsePipes(ValidationPipe)
  findAll() {
    console.log('5. Controller');
    return [];
  }
}
```

### Middleware vs Guards vs Interceptors vs Pipes -- The Definitive Comparison

This is the table you will refer back to most often. These four concepts are the most commonly confused parts of NestJS, and getting them wrong means putting code in the wrong layer.

| Aspect                       | Middleware                      | Guards                                       | Interceptors                                       | Pipes                                |
| ---------------------------- | ------------------------------- | -------------------------------------------- | -------------------------------------------------- | ------------------------------------ |
| **Primary Job**              | Raw request/response processing | Access control (yes/no)                      | Wrap handler execution (before + after)            | Validate/transform individual values |
| **Has Access To**            | `req`, `res`, `next`            | `ExecutionContext` (handler metadata, class) | `ExecutionContext` + handler's `Observable` return | The specific value being transformed |
| **Can Read Route Metadata?** | No                              | Yes (via `Reflector`)                        | Yes (via `Reflector`)                              | No                                   |
| **Can Modify Response?**     | Yes (via `res`)                 | No (only allow/deny)                         | Yes (via RxJS `map` operator)                      | No (only transforms input)           |
| **Can Short-Circuit?**       | Yes (skip `next()`)             | Yes (return `false`)                         | Yes (return cached `Observable`)                   | Yes (throw validation error)         |
| **Execution Order**          | 1st                             | 2nd                                          | 3rd (before) + 7th (after)                         | 4th                                  |
| **Scope Options**            | Global, route-specific          | Global, controller, method                   | Global, controller, method                         | Global, controller, method, param    |
| **DI Support**               | Class-based only                | Yes                                          | Yes                                                | Yes                                  |
| **Can Be Async?**            | Yes                             | Yes                                          | Yes (RxJS)                                         | Yes                                  |
| **Express/Fastify Aware?**   | Yes (platform-specific)         | No (uses `ExecutionContext`)                 | No (uses `ExecutionContext`)                       | No (value-level)                     |

**Decision Framework -- Where Does My Cross-Cutting Logic Go?**

```text theme={null}
"I need to log every request and its duration"
  --> Can I do it without NestJS context? YES --> Middleware (simple logging)
  --> Do I need to see the response? YES --> Interceptor (timing + response logging)

"I need to check if the user is authenticated"
  --> Guard (canActivate returns true/false)

"I need to check if the user has the 'admin' role"
  --> Guard with @Roles() metadata (uses Reflector to read route metadata)

"I need to validate the request body against a DTO"
  --> Pipe (ValidationPipe with class-validator)

"I need to wrap all responses in { data: ..., timestamp: ... }"
  --> Interceptor (uses RxJS map to transform the handler's return value)

"I need to add a correlation ID header to every response"
  --> Middleware (modifies res before handler runs) OR Interceptor (modifies after)

"I need to handle CORS, body parsing, or compression"
  --> Middleware (these are transport-level concerns)
```

<Warning>
  **Common Mistake**: Using middleware for authentication when you need route metadata (like roles). Middleware cannot read `@SetMetadata()` decorators because it runs before NestJS resolves the route handler. If you need to check roles or permissions, use a guard.
</Warning>

***

## 3.4 Validation & DTOs

Data Transfer Objects (DTOs) define the shape of data for requests and responses. Combined with validation, they ensure your API only accepts well-formed requests.

### Why Use DTOs?

* **Type Safety**: TypeScript knows the shape of your data
* **Validation**: Ensure data meets requirements
* **Documentation**: DTOs document your API contract
* **Transformation**: Can transform data automatically
* **Security**: Prevent invalid or malicious data

### Basic DTO

```typescript theme={null}
// dto/create-user.dto.ts
export class CreateUserDto {
  name: string;
  email: string;
  age: number;
}
```

### DTO with Validation

Install validation packages:

```bash theme={null}
npm install class-validator class-transformer
```

```typescript theme={null}
import { IsString, IsEmail, IsInt, Min, Max, IsOptional } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @MinLength(2)
  @MaxLength(50)
  name: string;

  @IsEmail()
  email: string;

  @IsInt()
  @Min(18)
  @Max(120)
  @IsOptional()
  age?: number;
}
```

### Common Validation Decorators

```typescript theme={null}
import {
  IsString,
  IsNumber,
  IsBoolean,
  IsEmail,
  IsUrl,
  IsDate,
  IsArray,
  IsOptional,
  IsNotEmpty,
  MinLength,
  MaxLength,
  Min,
  Max,
  Matches,
  IsEnum,
} from 'class-validator';

export class UserDto {
  @IsString()
  @IsNotEmpty()
  @MinLength(2)
  name: string;

  @IsEmail()
  email: string;

  @IsNumber()
  @Min(0)
  @Max(100)
  score: number;

  @IsOptional()
  @IsUrl()
  website?: string;

  @IsEnum(['admin', 'user', 'guest'])
  role: string;

  @Matches(/^[A-Z]{2}$/)
  countryCode: string;
}
```

### Global Validation Pipe

Apply validation globally in `main.ts`:

```typescript theme={null}
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,              // Strip properties that don't have decorators
      forbidNonWhitelisted: true,   // Throw error if non-whitelisted properties exist
      transform: true,              // Automatically transform payloads to DTO instances
      transformOptions: {
        enableImplicitConversion: true,  // Enable implicit type conversion
      },
    }),
  );
  
  await app.listen(3000);
}
```

### Custom Validation

Create custom validators:

```typescript theme={null}
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator';

export function IsStrongPassword(validationOptions?: ValidationOptions) {
  return function (object: Object, propertyName: string) {
    registerDecorator({
      name: 'isStrongPassword',
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      validator: {
        validate(value: any, args: ValidationArguments) {
          return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/.test(value);
        },
        defaultMessage(args: ValidationArguments) {
          return 'Password must be at least 8 characters with uppercase, lowercase, number, and special character';
        },
      },
    });
  };
}

// Usage
export class CreateUserDto {
  @IsString()
  @IsStrongPassword()
  password: string;
}
```

**Tip:** Always validate input to prevent security issues and bugs. Validation should happen at the controller level using DTOs and pipes.

***

## 3.5 Middleware

Middleware is executed before the route handler. Use it for logging, authentication, request transformation, CORS, and other cross-cutting concerns.

### What is Middleware?

Middleware functions have access to:

* Request object
* Response object
* Next function (to pass control to next middleware)

They can:

* Execute code before/after the route handler
* Modify request/response objects
* End the request-response cycle
* Call the next middleware in the stack

### Functional Middleware

Simple middleware as a function:

```typescript theme={null}
import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next();
}
```

### Class-Based Middleware

More powerful, can inject dependencies:

```typescript theme={null}
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
    next();
  }
}
```

### Middleware with Dependencies

```typescript theme={null}
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { ConfigService } from '../config/config.service';

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(private configService: ConfigService) {}

  use(req: Request, res: Response, next: NextFunction) {
    const apiKey = req.headers['x-api-key'];
    const validKey = this.configService.get('API_KEY');
    
    if (apiKey !== validKey) {
      return res.status(401).json({ message: 'Unauthorized' });
    }
    
    next();
  }
}
```

### Registering Middleware

Register in module using `configure` method:

```typescript theme={null}
import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { LoggerMiddleware } from './middleware/logger.middleware';

@Module({})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('users');  // Apply to all /users routes
  }
}
```

### Route-Specific Middleware

```typescript theme={null}
configure(consumer: MiddlewareConsumer) {
  consumer
    .apply(LoggerMiddleware)
    .forRoutes(
      { path: 'users', method: RequestMethod.GET },
      { path: 'users/:id', method: RequestMethod.GET },
    );
}
```

### Excluding Routes

```typescript theme={null}
configure(consumer: MiddlewareConsumer) {
  consumer
    .apply(LoggerMiddleware)
    .exclude(
      { path: 'users', method: RequestMethod.GET },
      'users/(.*)',
    )
    .forRoutes('*');
}
```

### Multiple Middleware

```typescript theme={null}
configure(consumer: MiddlewareConsumer) {
  consumer
    .apply(LoggerMiddleware, AuthMiddleware)
    .forRoutes('*');
}
```

### Global Middleware

Apply middleware globally in `main.ts`:

```typescript theme={null}
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './middleware/logger.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(logger);  // Global middleware
  await app.listen(3000);
}
```

**Tip:** Use middleware for cross-cutting concerns that apply to many routes. For route-specific logic, use guards or interceptors.

***

## 3.6 Guards

Guards determine whether a request should be handled by the route. They run after middleware but before the route handler. Use them for authentication and authorization.

### What are Guards?

Guards are like bouncers at a club. Their single job is to decide: "Should this request get in, or should I turn it away?" They run after middleware but before any other processing, which makes them ideal for authentication and authorization. If a guard says no, the request never reaches your controller -- saving you from duplicating permission checks in every route handler.

Guards return:

* `true` - Request proceeds to the next step in the pipeline
* `false` - Request is denied (NestJS automatically throws ForbiddenException)

### Basic Guard

```typescript theme={null}
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    return Boolean(request.headers['authorization']);
  }
}
```

### Using Guards

**Controller-level:**

```typescript theme={null}
@Controller('users')
@UseGuards(AuthGuard)
export class UsersController {
  @Get()
  findAll() {
    return [];
  }
}
```

**Method-level:**

```typescript theme={null}
@Controller('users')
export class UsersController {
  @Get()
  @UseGuards(AuthGuard)
  findAll() {
    return [];
  }
}
```

**Global Guard:**

```typescript theme={null}
// main.ts
app.useGlobalGuards(new AuthGuard());
```

### Execution Context

The `ExecutionContext` provides access to:

* Request object
* Response object
* Next function
* Handler (controller method)
* Class (controller class)

```typescript theme={null}
@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();
    const handler = context.getHandler();
    const controller = context.getClass();
    
    // Access request data
    const token = request.headers['authorization'];
    
    return Boolean(token);
  }
}
```

### Role-Based Guard

```typescript theme={null}
import { Injectable, CanActivate, ExecutionContext, SetMetadata } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

// Decorator to set roles
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
    
    if (!requiredRoles) {
      return true;  // No roles required
    }
    
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    
    return requiredRoles.some(role => user.roles?.includes(role));
  }
}

// Usage
@Controller('users')
@UseGuards(RolesGuard)
export class UsersController {
  @Delete(':id')
  @Roles('admin')
  remove(@Param('id') id: string) {
    return this.usersService.remove(id);
  }
}
```

### Async Guards

Guards can be async:

```typescript theme={null}
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = request.headers['authorization'];
    
    if (!token) {
      return false;
    }
    
    const user = await this.authService.validateToken(token);
    request.user = user;
    
    return Boolean(user);
  }
}
```

**Tip:** Use guards for permission checks, roles, and authentication logic. They're perfect for protecting routes and controlling access.

***

## 3.7 Interceptors

Interceptors can transform the result returned from a function, extend basic method behavior, or handle cross-cutting concerns like logging, caching, or response shaping.

### What are Interceptors?

Interceptors are like wrapping paper around a gift. They wrap your route handler, letting you run code before and after execution. Unlike middleware (which only sees the raw request/response), interceptors can see and transform the handler's return value using RxJS observables. This makes them powerful for cross-cutting concerns that need to act on the result.

Think of the logging interceptor pattern: "Start timer, let the handler run, stop timer, log the duration." Middleware cannot do this because it does not know when the handler finishes -- interceptors can, because they wrap the handler in an Observable pipeline.

Interceptors have access to:

* Execution context (which controller, which method, HTTP or WebSocket, etc.)
* Call handler (to invoke the route handler and get an Observable of the result)
* Full RxJS operator chain (map, tap, catchError, timeout, etc.)

They can:

* Execute code before/after method execution (timing, logging)
* Transform the result (wrap in `{ data: ..., timestamp: ... }` envelope)
* Transform exceptions (normalize error formats)
* Short-circuit the handler entirely (caching -- return cached data without calling the handler)

### Basic Interceptor

```typescript theme={null}
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');
    
    const now = Date.now();
    return next.handle().pipe(
      tap(() => console.log(`After... ${Date.now() - now}ms`)),
    );
  }
}
```

### Response Transformation Interceptor

Wrap responses in a consistent format:

```typescript theme={null}
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map(data => ({
        success: true,
        data,
        timestamp: new Date().toISOString(),
      })),
    );
  }
}
```

### Error Transformation Interceptor

Transform errors into consistent format:

```typescript theme={null}
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      catchError(err => {
        return throwError(() => ({
          success: false,
          error: err.message,
          statusCode: err.status || 500,
        }));
      }),
    );
  }
}
```

### Caching Interceptor

Cache responses. This is a simplified in-memory example to illustrate the pattern -- in production, use Redis or NestJS's built-in `@nestjs/cache-manager` module for distributed caching.

```typescript theme={null}
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class CacheInterceptor implements NestInterceptor {
  // WARNING: In-memory cache is per-process. If you run multiple instances
  // behind a load balancer, each instance has its own cache -- leading to
  // inconsistent responses. Use Redis for production caching.
  private cache = new Map();

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const key = request.url;
    
    // Cache hit: return stored data without calling the handler at all.
    // This is the "short-circuit" power of interceptors.
    if (this.cache.has(key)) {
      return of(this.cache.get(key));
    }
    
    // Cache miss: call the handler, then store the result.
    return next.handle().pipe(
      tap(data => {
        this.cache.set(key, data);
        // Clear cache after 5 minutes to prevent serving stale data
        setTimeout(() => this.cache.delete(key), 5 * 60 * 1000);
      }),
    );
  }
}
```

### Timeout Interceptor

Add timeout to requests:

```typescript theme={null}
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, RequestTimeoutException } from '@nestjs/common';
import { Observable, throwError, TimeoutError } from 'rxjs';
import { timeout, catchError } from 'rxjs/operators';

@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      timeout(5000),  // 5 second timeout
      catchError(err => {
        if (err instanceof TimeoutError) {
          return throwError(() => new RequestTimeoutException());
        }
        return throwError(() => err);
      }),
    );
  }
}
```

### Using Interceptors

**Controller-level:**

```typescript theme={null}
@Controller('users')
@UseInterceptors(LoggingInterceptor)
export class UsersController { }
```

**Method-level:**

```typescript theme={null}
@Get()
@UseInterceptors(TransformInterceptor)
findAll() {
  return [];
}
```

**Global Interceptor:**

```typescript theme={null}
// main.ts
app.useGlobalInterceptors(new TransformInterceptor());
```

**Tip:** Use interceptors for logging, response formatting, caching, and error transformation. They're powerful for cross-cutting concerns.

***

## 3.8 Error Handling

NestJS provides built-in exception filters and HTTP exceptions for consistent error handling.

### Built-in HTTP Exceptions

```typescript theme={null}
import {
  BadRequestException,
  UnauthorizedException,
  NotFoundException,
  ForbiddenException,
  NotAcceptableException,
  RequestTimeoutException,
  ConflictException,
  GoneException,
  HttpVersionNotSupportedException,
  PayloadTooLargeException,
  UnsupportedMediaTypeException,
  UnprocessableEntityException,
  InternalServerErrorException,
  NotImplementedException,
  BadGatewayException,
  ServiceUnavailableException,
  GatewayTimeoutException,
} from '@nestjs/common';

@Get(':id')
findOne(@Param('id') id: string) {
  const user = this.usersService.findOne(id);
  if (!user) {
    throw new NotFoundException('User not found');
  }
  return user;
}
```

### Custom Exception Messages

```typescript theme={null}
throw new BadRequestException('Invalid input');
throw new NotFoundException({ message: 'User not found', error: 'Not Found' });
```

### Exception Filters

Create custom exception filters:

```typescript theme={null}
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const message =
      exception instanceof HttpException
        ? exception.getResponse()
        : 'Internal server error';

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message,
    });
  }
}
```

**Using Exception Filters:**

```typescript theme={null}
@Controller('users')
@UseFilters(AllExceptionsFilter)
export class UsersController { }
```

**Global Exception Filter:**

```typescript theme={null}
// main.ts
app.useGlobalFilters(new AllExceptionsFilter());
```

***

## 3.9 Edge Cases in Request Handling

These are situations that catch experienced developers off guard.

**Edge Case 1: Multiple decorators on the same parameter**

You can stack pipes on a parameter, but they execute right-to-left (innermost first). `@Param('id', ParseIntPipe, CustomValidationPipe)` runs `ParseIntPipe` first, then passes the result to `CustomValidationPipe`. If `ParseIntPipe` converts "42" to `42`, `CustomValidationPipe` receives the number `42`, not the string "42".

**Edge Case 2: `@Res()` disables automatic response handling**

The moment you inject `@Res()` into a handler, NestJS stops managing the response. Your interceptors will not see the return value, and if you forget to call `res.json()` or `res.send()`, the request hangs indefinitely. Use `@Res({ passthrough: true })` if you need both `@Res()` access and NestJS's automatic response handling.

```typescript theme={null}
// Hangs forever -- NestJS does not send the response because @Res() is injected
@Get()
findAll(@Res() res: Response) {
  return this.usersService.findAll();  // Return value is IGNORED
}

// Fixed -- passthrough lets NestJS handle the return value
@Get()
findAll(@Res({ passthrough: true }) res: Response) {
  res.header('X-Custom', 'value');
  return this.usersService.findAll();  // NestJS sends this as JSON
}
```

**Edge Case 3: Validation errors from nested DTOs**

If your DTO contains a nested object, `class-validator` does not validate nested objects by default. You must add `@ValidateNested()` and `@Type(() => NestedDto)` from `class-transformer` to trigger recursive validation.

```typescript theme={null}
import { ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';

export class CreateOrderDto {
  @ValidateNested()
  @Type(() => AddressDto)   // Without this, the nested object is not validated
  shippingAddress: AddressDto;
}
```

**Edge Case 4: Guard ordering with multiple `@UseGuards()`**

When you apply multiple guards, they execute left-to-right. If the first guard rejects, subsequent guards never run. This matters for auth flows: put `JwtAuthGuard` (authentication) before `RolesGuard` (authorization), because checking roles without a valid user makes no sense.

```typescript theme={null}
@UseGuards(JwtAuthGuard, RolesGuard)   // Auth first, then roles
@Delete(':id')
@Roles('admin')
remove(@Param('id') id: string) { ... }
```

**Edge Case 5: File upload with `@Body()` and `@UploadedFile()`**

When handling multipart form data (file uploads), `@Body()` contains the text fields and `@UploadedFile()` contains the file. But `class-validator` cannot validate `@Body()` in multipart requests the same way it validates JSON -- all values arrive as strings. You need to transform them manually or use a custom pipe.

***

## 3.9 Best Practices

Following best practices ensures your controllers are maintainable and scalable.

### Keep Controllers Thin

Controllers should only handle HTTP concerns:

```typescript theme={null}
// Bad: Business logic in controller
@Post()
create(@Body() dto: CreateUserDto) {
  if (dto.email.includes('@')) {
    // validation logic
  }
  // business logic
  return { ... };
}

// Good: Controller delegates to service
@Post()
create(@Body() dto: CreateUserDto) {
  return this.usersService.create(dto);
}
```

### Use DTOs for All Input

Always validate input using DTOs:

```typescript theme={null}
@Post()
create(@Body() createDto: CreateUserDto) {
  return this.usersService.create(createDto);
}
```

### Organize Routes by Feature

Group related routes in feature modules:

```typescript theme={null}
// users/users.controller.ts
@Controller('users')
export class UsersController { }

// orders/orders.controller.ts
@Controller('orders')
export class OrdersController { }
```

### Use Appropriate HTTP Methods

```typescript theme={null}
@Get()      // Retrieve resources
@Post()     // Create resources
@Put()      // Replace resources
@Patch()    // Partially update resources
@Delete()   // Delete resources
```

### Return Consistent Response Shapes

Use interceptors to format responses consistently:

```typescript theme={null}
// All responses will be: { success: true, data: ... }
@UseInterceptors(TransformInterceptor)
@Controller('users')
export class UsersController { }
```

### Handle Errors Gracefully

Use built-in exceptions:

```typescript theme={null}
if (!user) {
  throw new NotFoundException('User not found');
}
```

### Document Your API

Use Swagger for API documentation:

```typescript theme={null}
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';

@ApiTags('users')
@Controller('users')
export class UsersController {
  @Get()
  @ApiOperation({ summary: 'Get all users' })
  @ApiResponse({ status: 200, description: 'Returns all users' })
  findAll() {
    return this.usersService.findAll();
  }
}
```

### Use Pipes for Transformation

```typescript theme={null}
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
  // id is already a number
  return this.usersService.findOne(id);
}
```

***

## 3.10 Summary

You've learned how to build robust, maintainable APIs using controllers and routing:

**Key Concepts:**

* **Controllers**: Handle HTTP requests and responses
* **Routing**: Define routes using decorators
* **DTOs**: Validate and transform input data
* **Middleware**: Cross-cutting concerns before route handlers
* **Guards**: Authentication and authorization
* **Interceptors**: Response transformation and logging
* **Error Handling**: Consistent error responses

**Best Practices:**

* Keep controllers thin
* Use DTOs for validation
* Organize routes by feature
* Use appropriate HTTP methods
* Handle errors gracefully
* Document your API

**Next Chapter:** Learn about providers, services, repository patterns, and domain-driven design in depth.

## Interview Deep-Dive

<AccordionGroup>
  <Accordion title="Compare middleware, guards, interceptors, and pipes in NestJS. When would you use each, and what happens if you put logic in the wrong layer?">
    **Strong Answer:**

    * Middleware runs first and has access to the raw Express/Fastify `req`, `res`, `next`. It does not know which NestJS route handler will execute. Use it for transport-level concerns: request logging, CORS, body parsing, rate limiting at the IP level. If you need to read NestJS route metadata (like `@Roles('admin')`), middleware cannot do it -- use a guard instead.
    * Guards run second and answer one question: "Should this request proceed?" They have access to the `ExecutionContext`, which knows the target controller and handler. This lets them read decorator metadata via `Reflector`. Use guards for authentication (is the JWT valid?) and authorization (does this user have the `admin` role?). Putting auth logic in middleware means you cannot use `@Roles()` decorators, and putting it in interceptors means unauthorized requests waste time on pre-processing before being rejected.
    * Pipes run after guards and transform/validate individual parameters. `ParseIntPipe` converts a string route param to a number. `ValidationPipe` validates a DTO against `class-validator` decorators. Putting validation in the service layer means invalid data travels further into your application before being rejected, and error messages are less HTTP-specific.
    * Interceptors wrap the handler and can execute code both before and after. They see the handler's return value via RxJS operators. Use them for response transformation (wrapping in a standard envelope), timing/logging, caching (short-circuit the handler if cache hit), and error mapping. Putting response transformation in the controller means every controller method has to repeat the wrapping logic.
    * The real-world consequence of putting logic in the wrong layer: I once worked on a project where authentication was in middleware. When we needed to add a `@Public()` decorator to skip auth on certain routes (like health checks), we could not -- middleware does not have access to handler metadata. We had to refactor all auth logic into a guard, which touched every module in the application.

    **Follow-up: If you apply both a global interceptor and a controller-level interceptor, what is the execution order?**

    Global interceptors run first (outermost), then controller-level, then method-level. On the response side, the order reverses (method-level response transformation runs first, then controller-level, then global). This is the "onion model" -- each interceptor wraps the next one. A global logging interceptor would log the total request time including all inner interceptor processing, while a method-level caching interceptor would only cache the result of that specific handler.
  </Accordion>

  <Accordion title="Your API returns inconsistent error responses -- some routes return different error shapes. How do you fix this across the entire application?">
    **Strong Answer:**

    * This is a global exception filter problem. NestJS has a built-in exception filter that formats `HttpException` subclasses into a standard shape by default. But if some routes throw raw errors, return custom objects, or use `@Res()` to bypass NestJS's response handling, you get inconsistent formats.
    * The fix: create a global `AllExceptionsFilter` that catches every exception (use `@Catch()` with no arguments) and formats it into a consistent shape. Register it in `main.ts` with `app.useGlobalFilters(new AllExceptionsFilter())`.
    * The filter should handle three cases: (1) `HttpException` subclasses -- extract the status and message from the exception. (2) Known error types (database errors, validation errors) -- map them to appropriate HTTP status codes. (3) Unknown errors -- return 500 with a generic message, log the actual error for debugging.
    * The important detail: if you register the filter globally in `main.ts` using `app.useGlobalFilters()`, it cannot inject dependencies (no DI). If your filter needs to inject a logger service, register it as a provider in `AppModule` using `APP_FILTER`: `{ provide: APP_FILTER, useClass: AllExceptionsFilter }`. This gives you DI support.
    * I also add a `correlationId` to every error response so the frontend can include it in bug reports, and the backend team can search logs by that ID.

    **Follow-up: How do exception filters interact with interceptors? If an interceptor has a catchError operator and there is also an exception filter, which one catches the error?**

    Interceptors' `catchError` runs first because interceptors wrap the handler. If the interceptor catches the error and returns a new observable (like a fallback value), the exception filter never sees the error. If the interceptor re-throws, the exception filter catches it. This ordering matters for retry logic -- an interceptor can retry a failed database call three times, and only if all retries fail does it re-throw, which the exception filter then formats into a user-friendly error response.
  </Accordion>

  <Accordion title="How would you implement API versioning in NestJS? What are the trade-offs between different versioning strategies?">
    **Strong Answer:**

    * NestJS supports four versioning strategies out of the box: URI (`/v1/users`), Header (`X-API-Version: 1`), Media Type (`Accept: application/vnd.myapi.v1+json`), and Custom. You enable versioning in `main.ts`: `app.enableVersioning({ type: VersioningType.URI })`.
    * URI versioning is the most common and the simplest to implement. It is visible in the URL, easy to test with curl, and easy to route in load balancers. The downside is URL proliferation -- every version creates a new set of URLs. Use `@Controller({ path: 'users', version: '1' })` or `@Version('1')` on individual methods.
    * Header versioning keeps URLs clean but is harder to test (you need to set headers in every request) and harder to cache (CDNs and proxies do not cache based on headers by default).
    * The real-world trade-off is between maintainability and backward compatibility. At one company, we used URI versioning and maintained v1 and v2 simultaneously for 18 months. The cost was maintaining two sets of controllers, two sets of DTOs, and two sets of tests. The lesson: version at the route level, not the module level. Both v1 and v2 controllers can call the same service -- only the request/response shape changes.
    * My recommendation: use URI versioning for external APIs (it is the most widely understood), and do not version internal microservice APIs (just coordinate deployments).

    **Follow-up: How do you deprecate and eventually remove an old API version without breaking clients?**

    Add a `Deprecation` response header to all v1 responses (`Deprecation: true`, `Sunset: 2026-06-01`) so clients can detect it programmatically. Log all v1 requests to identify which clients are still using the old version. Send direct communication to those clients with a migration timeline. After the sunset date, return 410 Gone for v1 endpoints instead of silently dropping them. The key is giving clients months of warning and making the migration path clear with a documented changelog of breaking changes between versions.
  </Accordion>

  <Accordion title="You need to build a guard that allows access only if the user owns the requested resource. How do you implement this ownership check?">
    **Strong Answer:**

    * This is an authorization guard that needs access to both the authenticated user (from the JWT) and the resource being requested (from the route parameter). The tricky part is that the guard runs before the controller handler, so you need to decide whether to query the database in the guard.
    * The implementation: the guard injects the `UsersService` and reads the `:id` parameter from the request. It then compares `request.user.id` (set by the JWT guard) with the requested `id`. If they match, the user owns the resource.
    * For simple ownership checks (user ID matches route param), the guard can be generic: read a metadata key that specifies which route param contains the owner ID, then compare it to the authenticated user ID. Use `@SetMetadata('ownerParam', 'id')` and read it with `Reflector`.
    * For complex ownership checks (e.g., "can this user edit this comment on this post?"), the guard needs to query the database. Inject the repository and call `comment = await commentRepo.findOne(commentId)`, then check `comment.authorId === user.id`. This adds a database query to every request, so consider caching.
    * The important design decision: should the guard throw 403 Forbidden or 404 Not Found? If a user requests a resource they do not own, returning 403 reveals that the resource exists (information leakage). Returning 404 is more secure -- the user does not even know the resource exists. For public APIs, I prefer 404. For internal admin tools, 403 is more helpful for debugging.

    **Follow-up: How do you handle the case where an admin should be able to access any resource, bypassing the ownership check?**

    Add a role check at the top of the guard: `if (user.roles.includes('admin')) return true`. Or use NestJS's `Reflector` to compose multiple decorators: `@Roles('admin')` on the method allows admins through the `RolesGuard`, and `@OwnerOnly('id')` handles ownership for non-admin users. The execution order matters -- put `RolesGuard` before `OwnershipGuard` in the `@UseGuards()` array so admins short-circuit before the ownership database query runs.
  </Accordion>
</AccordionGroup>
