En esta página
Controladores y rutas HTTP
Los controladores en NestJS
Los controladores son la capa de entrada de tu API. Son responsables de recibir peticiones HTTP, delegar el trabajo pesado a los servicios y devolver una respuesta apropiada. Un principio clave: los controladores no deben contener lógica de negocio. Su única responsabilidad es mapear peticiones HTTP a llamadas de servicio.
Declarando un controlador
El decorador @Controller() acepta un prefijo de ruta opcional. Todas las rutas definidas dentro de la clase tendrán ese prefijo automáticamente:
@Controller('api/v1/usuarios') // todas las rutas empiezan con /api/v1/usuarios
export class UsuariosController {}También puedes usar @Controller({ path: 'usuarios', version: '1' }) si usas el versionado de APIs de NestJS.
Verbos HTTP y rutas
NestJS proporciona decoradores para todos los verbos HTTP estándar:
| Decorador | Verbo HTTP | Uso típico |
|---|---|---|
@Get() |
GET | Leer recursos |
@Post() |
POST | Crear recursos |
@Put() |
PUT | Reemplazar recursos completos |
@Patch() |
PATCH | Actualizar parcialmente |
@Delete() |
DELETE | Eliminar recursos |
@Options() |
OPTIONS | Preflight CORS |
@Head() |
HEAD | Metadatos sin cuerpo |
@All() |
Todos | Catch-all para cualquier verbo |
Cada decorador acepta una ruta relativa al prefijo del controlador:
@Controller('articulos')
export class ArticulosController {
@Get() // GET /articulos
@Get(':id') // GET /articulos/42
@Get(':id/tags') // GET /articulos/42/tags
@Post() // POST /articulos
@Delete(':id') // DELETE /articulos/42
}Extrayendo datos de la petición
@Param — Parámetros de ruta
@Get(':categoria/:id')
findOne(
@Param('categoria') categoria: string,
@Param('id') id: string,
) {
return { categoria, id };
}
// Para obtener todos los parámetros como objeto:
@Get(':categoria/:id')
findOne(@Param() params: Record<string, string>) {
return params; // { categoria: 'ropa', id: '42' }
}@Query — Parámetros de consulta
// GET /productos?pagina=2&limite=20&orden=precio_asc
@Get()
findAll(
@Query('pagina') pagina = '1',
@Query('limite') limite = '20',
@Query('orden') orden?: string,
) {
return this.service.findAll({ pagina, limite, orden });
}@Body — Cuerpo de la petición
@Post()
create(@Body() crearDto: CrearUsuarioDto) {
return this.service.create(crearDto);
}
// Extraer solo una propiedad del cuerpo:
@Patch(':id/estado')
cambiarEstado(
@Param('id') id: string,
@Body('activo') activo: boolean,
) {
return this.service.cambiarEstado(id, activo);
}@Headers — Cabeceras HTTP
@Get()
findAll(@Headers('authorization') auth: string) {
// Lectura manual de cabeceras (para casos especiales)
return this.service.findAll();
}Data Transfer Objects (DTOs)
Los DTOs son clases simples de TypeScript que definen la forma esperada de los datos de entrada. Son fundamentales para tener tipado estricto y son la base para la validación automática que veremos en la lección de Pipes.
// dto/crear-usuario.dto.ts
export class CrearUsuarioDto {
nombre: string;
email: string;
password: string;
rol?: 'usuario' | 'admin';
}La convención de nombres en NestJS es CrearXxxDto para creación y ActualizarXxxDto para actualizaciones. Muchos proyectos usan el paquete @nestjs/mapped-types para reutilizar DTOs:
import { PartialType } from '@nestjs/mapped-types';
import { CrearUsuarioDto } from './crear-usuario.dto';
// ActualizarUsuarioDto tiene todas las propiedades de CrearUsuarioDto pero opcionales
export class ActualizarUsuarioDto extends PartialType(CrearUsuarioDto) {}Códigos de estado HTTP
Por defecto, NestJS devuelve 200 OK para GET, PUT, PATCH, DELETE y 201 Created para POST. Para personalizar el código de estado, usa @HttpCode:
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT) // 204
remove(@Param('id') id: string) {
return this.service.remove(id);
}
@Post('login')
@HttpCode(HttpStatus.OK) // 200 en lugar del 201 por defecto
login(@Body() loginDto: LoginDto) {
return this.authService.login(loginDto);
}HttpStatus es un enum de NestJS con todos los códigos de estado HTTP estándar, lo que mejora la legibilidad respecto a usar números directamente.
Manejo de rutas comodín
NestJS soporta wildcards en las rutas usando la sintaxis de Express:
@Get('ab*cd')
findAll() {
return 'Esta ruta responde a abcd, ab_cd, abecd, etc.';
}
// Parámetros opcionales
@Get(':id?')
findOne(@Param('id') id?: string) {
return id ? this.service.findOne(id) : this.service.findAll();
}Prefijos globales de ruta
En lugar de poner /api/v1 en cada controlador, puedes configurar un prefijo global en main.ts:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api/v1'); // todas las rutas tendrán este prefijo
await app.listen(3000);
}Si necesitas excluir algunas rutas del prefijo global (como /health o /):
app.setGlobalPrefix('api/v1', {
exclude: [{ path: 'health', method: RequestMethod.GET }],
});Respuestas asíncronas
NestJS maneja promesas y observables de RxJS de forma transparente. Puedes devolver una promesa o un Observable directamente desde un método de controlador:
@Get()
async findAll(): Promise<Producto[]> {
return this.service.findAll(); // retorna una Promise<Producto[]>
}
@Get(':id')
findOne(@Param('id') id: string): Observable<Producto> {
return this.service.findOne(id); // retorna un Observable<Producto>
}NestJS resolverá automáticamente la promesa o el observable antes de enviar la respuesta al cliente.
Versionado de API
NestJS tiene soporte integrado para versionar tu API de varias formas:
// main.ts — activar versionado por URI
import { VersioningType } from '@nestjs/common';
app.enableVersioning({
type: VersioningType.URI,
});
// En el controlador:
@Controller({ path: 'usuarios', version: '1' })
export class UsuariosV1Controller {}
@Controller({ path: 'usuarios', version: '2' })
export class UsuariosV2Controller {}Esto generará rutas como /v1/usuarios y /v2/usuarios automáticamente.
Los controladores son la puerta de entrada a tu API, pero solos no hacen mucho. En la próxima lección aprenderemos sobre los providers y servicios —el lugar donde realmente vive la lógica de negocio de tu aplicación.
Inicia sesión para guardar tu progreso