controllers
SKILL.md
NestJS Controllers
When to Use This Skill
Use this skill when:
- Creating REST API endpoints
- Handling HTTP requests (GET, POST, PUT, DELETE, PATCH)
- Working with route parameters, query parameters, or request bodies
- Setting up routing and path prefixes
- Handling redirects, headers, or status codes
- Working with async/await in route handlers
What are Controllers?
Controllers are responsible for handling incoming requests and returning responses to the client. They use decorators to map routes to handler methods.
Basic Controller
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
Key Points:
@Controller('cats')- Sets route prefix/cats@Get()- Maps GET requests to this method- Returns value automatically serialized to JSON (objects/arrays)
- Must register controller in module's
controllersarray
HTTP Method Decorators
import { Controller, Get, Post, Put, Delete, Patch } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll() {
return [];
}
@Post()
create() {
return 'Created';
}
@Put(':id')
update() {
return 'Updated';
}
@Delete(':id')
remove() {
return 'Deleted';
}
@Patch(':id')
patch() {
return 'Patched';
}
}
Route Parameters
import { Controller, Get, Param } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get(':id')
findOne(@Param('id') id: string) {
return `This action returns cat #${id}`;
}
// Multiple parameters
@Get(':category/:id')
findByCategory(
@Param('category') category: string,
@Param('id') id: string
) {
return `Category: ${category}, ID: ${id}`;
}
// All params as object
@Get(':id')
findOneAlt(@Param() params: { id: string }) {
return `Cat #${params.id}`;
}
}
Query Parameters
import { Controller, Get, Query } from '@nestjs/common';
@Controller('cats')
export class CatsController {
// /cats?limit=10&offset=0
@Get()
findAll(
@Query('limit') limit: string,
@Query('offset') offset: string
) {
return `Limit: ${limit}, Offset: ${offset}`;
}
// All query params as object
@Get()
findAllAlt(@Query() query: { limit: string; offset: string }) {
return query;
}
}
Request Body
import { Controller, Post, Body } from '@nestjs/common';
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
@Controller('cats')
export class CatsController {
@Post()
create(@Body() createCatDto: CreateCatDto) {
return `Creating cat: ${createCatDto.name}`;
}
// Specific property
@Post()
createAlt(@Body('name') name: string) {
return `Creating cat: ${name}`;
}
}
Request Object
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get()
findAll(@Req() request: Request) {
return request.headers;
}
}
Available Request Decorators:
@Request(), @Req()- Full request object@Response(), @Res()- Full response object (use with caution)@Next()- Next function@Session()- Session object@Param(key?)- Route parameters@Body(key?)- Request body@Query(key?)- Query parameters@Headers(key?)- Request headers@Ip()- Client IP address@HostParam()- Host parameters
Status Codes
import { Controller, Post, HttpCode, HttpStatus } from '@nestjs/common';
@Controller('cats')
export class CatsController {
// Default: 200 for GET, 201 for POST
@Post()
create() {
return 'Created';
}
// Custom status code
@Post()
@HttpCode(HttpStatus.NO_CONTENT)
createNoContent() {
// Returns 204
}
@Post()
@HttpCode(204)
createAlt() {
// Also returns 204
}
}
Response Headers
import { Controller, Post, Header } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Post()
@Header('Cache-Control', 'no-store')
@Header('X-Custom-Header', 'value')
create() {
return 'Created with custom headers';
}
}
Redirection
import { Controller, Get, Redirect } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get('docs')
@Redirect('https://docs.nestjs.com', 301)
getDocs() {
// Redirects to docs
}
// Dynamic redirect
@Get('search')
@Redirect()
search(@Query('term') term: string) {
return {
url: `https://google.com/search?q=${term}`,
statusCode: 302
};
}
}
Route Wildcards
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
// Matches: /cats/profile, /cats/p, /cats/prxfile
@Get('ab*cd')
findWildcard() {
return 'Wildcard route';
}
}
Supported wildcards: *, ?, +, ()
Sub-Domain Routing
import { Controller, Get, HostParam } from '@nestjs/common';
@Controller({ host: ':account.example.com' })
export class AccountController {
@Get()
getInfo(@HostParam('account') account: string) {
return `Account: ${account}`;
}
}
Async Handlers
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
async findAll(): Promise<Cat[]> {
return await this.catsService.findAll();
}
// Using Observables (RxJS)
@Get()
findAllRx(): Observable<Cat[]> {
return of([]);
}
}
DTOs (Data Transfer Objects)
// create-cat.dto.ts
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
// cats.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
@Controller('cats')
export class CatsController {
@Post()
create(@Body() createCatDto: CreateCatDto) {
return createCatDto;
}
}
Why use classes instead of interfaces?
- Classes are preserved at runtime (interfaces are not)
- Enables validation with
class-validator - Works with Pipes for transformation
Complete Controller Example
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
import { CatsService } from './cats.service';
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Get()
findAll(@Query('limit') limit = 10) {
return this.catsService.findAll(limit);
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.catsService.findOne(id);
}
@Post()
@HttpCode(HttpStatus.CREATED)
create(@Body() createCatDto: CreateCatDto) {
return this.catsService.create(createCatDto);
}
@Put(':id')
update(
@Param('id') id: string,
@Body() updateCatDto: UpdateCatDto
) {
return this.catsService.update(id, updateCatDto);
}
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
remove(@Param('id') id: string) {
return this.catsService.remove(id);
}
}
Registering Controllers
Controllers must be registered in a module:
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
Best Practices
- Keep controllers thin - Delegate business logic to services
- Use DTOs - Define classes for request/response data
- Validate input - Use
ValidationPipewithclass-validator - Use dependency injection - Inject services via constructor
- Handle errors properly - Throw appropriate HTTP exceptions
- Use async/await - For asynchronous operations
- Document APIs - Use
@nestjs/swaggerfor OpenAPI docs - Consistent naming - Use standard REST conventions
Common Patterns
Resource Controller Pattern
nest generate resource cats
This generates:
- Module
- Controller with CRUD routes
- Service
- DTOs (create, update)
- Entity
Nested Routes
@Controller('users/:userId/posts')
export class PostsController {
@Get()
findUserPosts(@Param('userId') userId: string) {
return this.postsService.findByUser(userId);
}
}
Versioning
import { Controller, Version } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Version('1')
@Get()
findAllV1() {
return 'V1';
}
@Version('2')
@Get()
findAllV2() {
return 'V2';
}
}
Weekly Installs
1
Repository
ramziddin/ccpluginsGitHub Stars
1
First Seen
6 days ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
warp1