Backend
NestJS
- Backend
- typescript
- node.js
-
[Documentation NestJS - A progressive Node.js framework](https://docs.nestjs.com/) -
[OpenAPI (Swagger) NestJS - A progressive Node.js framework](https://docs.nestjs.com/openapi/introduction)
Introducción general
- Framework progresivo para Node.js basado en TypeScript, inspirado en Angular.
- Promueve mantenibilidad, modularidad, testabilidad y una arquitectura limpia.
- Construido sobre Express (por defecto) o Fastify (para mejor rendimiento).
- Enfoque basado en decoradores y metadatos, aprovechando la inyección de dependencias.
Elementos principales
- Controllers → manejan rutas y peticiones HTTP.
- Services (Providers) → encapsulan la lógica de negocio.
- Entities y DTOs → estructuras de datos para persistencia y transporte.
- Middleware → lógica intermedia en el flujo de petición.
- Pipes → validación y transformación de datos.
- Guards → control de acceso y autorización.
- Interceptors → manipulación de respuesta o comportamiento en runtime.
- Decoradores personalizados → metaprogramación reutilizable.
- CLI → automatiza creación de módulos, controladores, servicios y recursos.
- Testing → integración nativa con Jest.
Recursos introductorios
- ¿Qué es NESTJS? 🤔 Nest JS vs Express Parte 1 - YouTube
- Controller: usa decoradores (
@Controller,@Get,@Post) y define rutas. - Service: se inyecta mediante el constructor (
constructor(private readonly service: ...)). - Base de datos: se modela y conecta desde los services.
- Postman: usado para comprobar endpoints.
- CLI Resource:
- Comando:
nest generate resource <nombre> - Crea automáticamente clases, métodos y respuestas.
- Permite acceso directo a rutas pasando strings en el controller.
- Comando:
- Swagger: integración directa para documentación.
- OpenAPI (Swagger) - NestJS - A progressive Node.js framework
- Decorador
@ApiTagsorganiza los endpoints. - Permite crear API tipadas.
- Controller: usa decoradores (
NestJS + Next.js + Prisma (Fullstack moderno)
- Nextjs y Nestjs - Aplicación CRUD (con Typescript, Shadcn, prisma y más) - YouTube
-
[Get started with Prisma Prisma Documentation](https://www.prisma.io/docs/getting-started) - NestJS: backend API (REST o GraphQL).
- Next.js: frontend React SSR/SSG.
- Prisma ORM: acceso tipado a base de datos.
- CRUD completo (Create, Read, Update, Delete).
- Integración con Shadcn/UI.
- Ideal para proyectos con frontend moderno y backend modular.
Estructura de proyectos
- Si tuviera que empezar un nuevo proyecto de NestJS usaría esto! - YouTube
- Mejores prácticas de estructura y rendimiento:
- Evitar mala estructura de carpetas que viole principios SOLID.
- Separar tests unitarios y tests e2e con convención clara en
package.json. - Optimizar para producción (especialmente en Kubernetes).
- Usar SWC como compilador (sustituye
tsc, más rápido y eficiente). - Sustituir Express por Fastify → menor latencia, más rendimiento.
- Mantener niveles de log configurables.
- Implementar husky para pre-commit hooks y merge coverage en CI/CD.
- Templates personalizados:
- Cambiar el template por defecto del comando
nest newpara usar una estructura personalizada.
Sistema de pagos con Arquitectura Hexagonal (Clean Architecture)
- GitHub - AlbertHernandez/nestjs-hexagonal-architecture-example
-
[Arquitectura Hexagonal en NestJS Clean Architecture - YouTube](https://www.youtube.com/watch?v=4_4p5Ojs5XA) - Ideal para apps grandes y con dominio complejo.
- Crear y listar pagos, aplicando principios de Domain-Driven Design.
Arquitectura Hexagonal
- Divide el sistema en Dominios, Aplicación e Infraestructura.
- Las capas internas no dependen de las externas.
- Comunicación mediante Puertos (Interfaces) y Adaptadores (Implementaciones).
- Uso del patrón Unit of Work para operaciones atómicas (por ejemplo, decoradores para transacciones en MongoDB o PostgreSQL).
- Decorador de transacción (ejemplo implementado en Middleware) 👆
Dominio (Domain Layer)
- Carpeta:
context/payments/domain - Entidad
payment.ts- Define primitivos (ID, cliente, cantidad) con interfaces.
- Construcción de la entidad usando atributos tipados.
- Métodos:
static createPayment()toPrimitives()→ convierte el objeto a datos simples.
- Uso de uuid para IDs únicos.
- Ejemplo de creación de pago:
const payment = Payment.create({
id: uuid(),
customerId: '123',
amount: 100
});
Clases abstractas y repositorios
- Archivo:
payment.repository.ts
export abstract class PaymentRepository {
abstract save(payment: Payment): Promise<void>;
abstract findById(id: string): Promise<Payment | null>;
}
```
- **Excepciones personalizadas:**
```ts
export class PaymentNotFoundException extends Error {
constructor(id: string) {
super(`Payment with ID ${id} not found`);
}
}
```
---
## Casos de uso (Application Layer)
- Ejemplo: `create-payment.usecase.ts`
```ts
export interface CreatePaymentDto {
id: string;
customerId: string;
amount: number;
}
```
```ts
export class CreatePaymentUseCase {
constructor(
private readonly repository: PaymentRepository
) {}
async execute(dto: CreatePaymentDto): Promise<PaymentPrimitives> {
const payment = Payment.create(dto);
await this.repository.save(payment);
return payment.toPrimitives();
}
}
```
- El caso de uso construye el repositorio con métodos abstractos.
- Devuelve objetos compuestos únicamente por primitivos.
- Cada caso de uso orquesta la lógica entre dominio e infraestructura.
---
## Infraestructura (Infra Layer)
- Compuesta por módulos, controladores y servicios inyectables.
- **DTOs** para transporte de datos HTTP:
- `payment_http-dto.ts`
- Ejemplo básico:
```ts
@Controller('payments')
export class PaymentController {
constructor(private readonly createPayment: CreatePaymentUseCase) {}
@Post()
async create(@Body() dto: CreatePaymentDto) {
return this.createPayment.execute(dto);
}
}
```
- **Capa agnóstica de la base de datos**.
- **Carpeta shared:** código y utilidades comunes.
- Configuración mediante `@Module()` con `providers`.
- Implementación de repositorios concretos que cumplen las interfaces del dominio.
### Pendientes / TODOs
- Endpoint de creación y listado de pagos.
- Injectable personalizado para casos de uso.
- Verificación de endpoints en Postman.
- Manejo de excepciones HTTP (`NotFoundException`, `BadRequestException`, etc.)
- Separación entre **DTOs de capa HTTP** y **DTOs de aplicación**.
- Repositorio lanza excepción si no existe el pago.
---
# NestJS – Avanzado y Ecosistema
- NestJS
- [Backend](/backend/backend/)
- [typescript](/software%20engineering/typescript/)
---
## Core avanzado y ejecución interna
- NestJS está construido sobre un **módulo de inyección de dependencias (IoC Container)**.
- Permite gestionar el **ciclo de vida** de instancias y sus dependencias.
- Usa **reflexión** (metadata) y el decorador `@Injectable()` para registrar providers.
- **Módulos dinámicos**
- Permiten registrar dependencias de forma condicional o configurable.
- Ejemplo: `ConfigModule.forRoot({ isGlobal: true })`
- Útiles para configurar autenticación, conexión a DB o servicios externos.
- **Scopes de providers**
- `Scope.DEFAULT` → singleton.
- `Scope.REQUEST` → nueva instancia por request.
- `Scope.TRANSIENT` → nueva instancia por cada inyección.
---
## Inyección de dependencias avanzada
- **Custom Providers:**
- Se puede inyectar usando `useClass`, `useExisting`, `useValue`, `useFactory`.
- Ejemplo:
```ts
@Module({
providers: [
{
provide: 'PAYMENT_REPOSITORY',
useClass: PostgresPaymentRepository,
},
],
})
```
- **Circular dependencies:**
- Resolver con `forwardRef(() => ServiceA)` cuando dos módulos dependen entre sí.
- **Value providers:** útil para constantes globales o tokens de configuración.
---
## Middleware, Guards, Pipes e Interceptors
### Middleware
- Ejecutan antes de llegar a los controladores.
- Implementan `NestMiddleware` y se aplican con `app.use()` o en el módulo.
```ts
app.use(loggerMiddleware);
```
### Guards
- Controlan acceso y autorización.
- Retornan `true` o `false` según permisos o roles.
```ts
@UseGuards(AuthGuard)
@Get('profile')
getProfile() {}
```
### Pipes
- Validan y transforman los datos entrantes.
- `ValidationPipe`, `ParseIntPipe`, o pipes personalizados.
- Se aplican globalmente:
```ts
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
```
### Interceptors
- Modifican las respuestas, loguean o miden tiempos.
- Ejemplo: LoggingInterceptor, TransformInterceptor.
```ts
@UseInterceptors(LoggingInterceptor)
@Get()
findAll() {}
```
---
## Microservicios y comunicación distribuida
- NestJS permite crear **microservicios nativos** mediante el módulo `@nestjs/microservices`.
- Soporta distintos **transportes**:
- `TCP`, `Redis`, `MQTT`, `NATS`, `Kafka`, `gRPC`.
- Ejemplo de microservicio con **Redis transport**:
```ts
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.REDIS,
options: { url: 'redis://localhost:6379' },
});
app.listen();
```
- Uso de `@MessagePattern()` en lugar de `@Controller()` para procesar eventos.
- Integración natural con patrones **CQRS** y **Event Sourcing**.
---
## CQRS y Event Sourcing
- **CQRS (Command Query Responsibility Segregation)** separa operaciones de lectura y escritura.
- Compatible con `@nestjs/cqrs`.
- **Commands:** ejecutan acciones que cambian estado.
- **Queries:** obtienen datos sin mutarlos.
- **Events:** notifican cambios.
- Ejemplo:
```ts
export class CreatePaymentCommand {
constructor(public readonly dto: CreatePaymentDto) {}
}
```
```ts
@CommandHandler(CreatePaymentCommand)
export class CreatePaymentHandler {
constructor(private readonly repo: PaymentRepository) {}
async execute(command: CreatePaymentCommand) {
const payment = Payment.create(command.dto);
await this.repo.save(payment);
}
}
```
- El patrón **Event Sourcing** registra los eventos del dominio en lugar del estado final, ideal para auditoría y resiliencia.
---
## GraphQL en NestJS
- Soporte nativo mediante `@nestjs/graphql`.
- Basado en `apollo-server` o `Mercurius` (para Fastify).
- Tipado automático a partir de DTOs con decoradores:
```ts
@Resolver(() => User)
export class UserResolver {
constructor(private userService: UserService) {}
@Query(() => [User])
async users() {
return this.userService.findAll();
}
}
```
- Modos:
- **Code First:** esquemas generados desde clases TypeScript.
- **Schema First:** esquemas definidos en archivos `.graphql`.
---
## WebSockets y Gateway en tiempo real
- Basado en `@WebSocketGateway` y `@SubscribeMessage`.
- Compatible con `socket.io` y `ws`.
```ts
@WebSocketGateway()
export class ChatGateway {
@SubscribeMessage('message')
handleMessage(client: Socket, payload: string): void {
client.broadcast.emit('message', payload);
}
}
```
- Ideal para chats, notificaciones, juegos o sistemas colaborativos.
---
## Testing en NestJS
- Integración directa con **Jest**.
- Tipos de tests:
- **Unit tests** → testean servicios o lógica de negocio aislada.
- **e2e tests** → prueban módulos completos usando `@nestjs/testing`.
- Ejemplo de test unitario:
```ts
describe('PaymentsService', () => {
let service: PaymentsService;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [PaymentsService],
}).compile();
service = module.get<PaymentsService>(PaymentsService);
});
it('should create a payment', () => {
expect(service.create({ id: '1', amount: 10 })).toBeDefined();
});
});
```
- Integrar **coverage reports**, **mocking de repositorios**, y **TestContainers** para bases de datos reales en CI/CD.
---
## Optimización y despliegue
- **Compilación:** usar SWC o esbuild para tiempos de build menores.
- **Monitorización:** integrar con Prometheus o OpenTelemetry.
- **Despliegue:**
- [Kubernetes](/virtualizacion/kubernetes/) con `Helm` o `kustomize`.
- Escalabilidad horizontal con microservicios y gateways.
- Integrar pipelines CI/CD para lint, test y build automáticos.
---
## Ecosistema y extensiones útiles
- **NestJS CLI Plugins:**
- `@nestjs/config` → variables de entorno.
- `@nestjs/swagger` → documentación automática.
- `@nestjs/passport` → autenticación JWT, OAuth.
- `@nestjs/terminus` → health checks.
- `@nestjs/schedule` → cron jobs.
- **Integraciones recomendadas:**
- Prisma o TypeORM (ORMs)
- Redis (cache, pub/sub)
- [RabbitMQ](/backend/rabbitmq/) o Kafka (mensajería)
- [OpenTelemetry](/monitoreo/opentelemetry/) (monitorización)
- **Plantillas de referencia:**
- [nestjs-prisma-starter](https://github.com/notiz-dev/nestjs-prisma-starter)
- [nestjs-boilerplate](https://github.com/samchon/nestia)
---
## Buenas prácticas y principios
- Mantener cada **módulo con alta cohesión y bajo acoplamiento**.
- Evitar lógica de negocio en controladores.
- Usar **DTOs** para validar toda entrada externa.
- Desacoplar infraestructura (ORMs, HTTP) del dominio.
- Añadir **decoradores personalizados** para validaciones o logging.
- Modularizar según **bounded contexts** (DDD).
- Monitorizar tiempos con **Interceptors** y logs estructurados.
- Mantener configuración centralizada con `ConfigService`.
---
## Relacionado
- Arquitectura Hexagonal
- CQRS y Event Sourcing
- Microservicios
- Backend Testing
- [GraphQL](/backend/graphql/)
- [Kubernetes](/virtualizacion/kubernetes/)
# NestJS – Arquitectura Aplicada con Patrones Avanzados
- NestJS
- Arquitectura Hexagonal
- CQRS y Event Sourcing
- DDD
- Backend Patterns
---
## Enfoque General
Esta arquitectura integra **principios de DDD, CQRS y Event Sourcing** sobre un stack moderno con **Prisma**, **Swagger**, **Auth modular**, y **observabilidad distribuida**.
El objetivo es mantener **alta escalabilidad, trazabilidad y mantenibilidad** en sistemas complejos o de misión crítica.
---
## 1. Estructura Modular
### Capas Principales
- **Domain:** entidades, value objects, agregados y eventos del dominio.
- **Application:** casos de uso (commands, queries, handlers).
- **Infrastructure:** persistencia, adaptadores externos, Prisma, colas, APIs.
- **Interface:** controladores, resolvers, DTOs, validaciones y Swagger.
### Ejemplo de estructura:
src/ ├── domain/ │ ├── user.entity.ts │ ├── events/ │ └── value-objects/ ├── application/ │ ├── commands/ │ ├── queries/ │ ├── handlers/ │ └── services/ ├── infrastructure/ │ ├── prisma/ │ ├── repositories/ │ └── adapters/ └── interface/ ├── controllers/ ├── graphql/ └── dto/
`
---
## 2. Integración de CQRS + Event Sourcing
- Se utiliza `@nestjs/cqrs` para separar lectura/escritura y capturar eventos.
- **CommandHandlers** ejecutan acciones de negocio.
- **EventHandlers** persisten o publican eventos.
- **EventStore** centraliza los eventos (por ejemplo, con PostgreSQL o Kafka).
### Ejemplo de Command + Event
```ts
export class CreateUserCommand {
constructor(public readonly dto: CreateUserDto) {}
}
@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
constructor(
private readonly repo: UserRepository,
private readonly eventBus: EventBus
) {}
async execute(command: CreateUserCommand) {
const user = User.create(command.dto);
await this.repo.save(user);
this.eventBus.publish(new UserCreatedEvent(user.id));
}
}
`
Event Handler
@EventsHandler(UserCreatedEvent)
export class UserCreatedHandler implements IEventHandler<UserCreatedEvent> {
handle(event: UserCreatedEvent) {
console.log('User created:', event.userId);
}
}
3. Persistencia con Prisma
- Prisma se usa como capa de infraestructura, desacoplada del dominio.
- Los repositorios implementan interfaces de dominio (
IUserRepository) para facilitar testeo y mocks.
@Injectable()
export class PrismaUserRepository implements IUserRepository {
constructor(private prisma: PrismaService) {}
async save(user: User): Promise<void> {
await this.prisma.user.create({ data: user.toPrimitives() });
}
async findById(id: string): Promise<User | null> {
const data = await this.prisma.user.findUnique({ where: { id } });
return data ? User.fromPrimitives(data) : null;
}
}
4. Documentación con Swagger
- Integrar desde
main.ts:
const config = new DocumentBuilder()
.setTitle('Advanced API')
.setDescription('API with CQRS + Prisma + Auth + Observability')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
- Genera documentación interactiva y permite probar endpoints autenticados.
- Combinar con decoradores
@ApiTags,@ApiBearerAuth,@ApiResponse.
5. Autenticación y Autorización Avanzadas
- Uso de JWT + Passport para autenticación modular.
AuthModuledefine estrategias (JwtStrategy,LocalStrategy).- Guards controlan acceso según roles o claims:
@UseGuards(JwtAuthGuard, RolesGuard)
@Get('me')
@ApiBearerAuth()
getProfile(@Request() req) {
return req.user;
}
- Integrar RBAC (Role-Based Access Control) o ABAC (Attribute-Based Access Control) mediante decorators personalizados.
6. Observabilidad, Logging y Tracing
- Integración con OpenTelemetry y Prometheus.
- Cada request puede generar un trace distribuido con
traceId. - Configuración típica:
import { OpenTelemetryModule } from 'nestjs-otel';
@Module({
imports: [OpenTelemetryModule.forRoot()],
})
export class ObservabilityModule {}
- Métricas expuestas en
/metricspara Prometheus. - Logging estructurado con
nestjs-pinoowinston:
app.useLogger(app.get(Logger));
7. Patrones Compuestos
Saga + EventBus + Repository
-
Saga coordina varios pasos distribuidos.
- Escucha eventos y dispara comandos según el flujo.
@Saga()
export class PaymentSaga {
userCreated = (events$: Observable<UserCreatedEvent>) =>
events$.pipe(
map(event => new InitializePaymentCommand(event.userId))
);
}
Factory + Strategy dentro del dominio
- Factory crea entidades o agregados coherentes.
- Strategy aplica diferentes políticas de negocio (p.e. cálculo de comisiones).
export interface PaymentStrategy {
calculate(amount: number): number;
}
export class PremiumPaymentStrategy implements PaymentStrategy {
calculate(amount: number) {
return amount * 0.9;
}
}
8. Testing de Arquitectura Completa
- Unit testing con
jest.mock()para repositorios. - Integration testing con
Test.createTestingModule. - e2e testing simulando el flujo CQRS completo con
supertest.
Ejemplo e2e:
describe('User lifecycle (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/POST user', async () => {
return request(app.getHttpServer())
.post('/users')
.send({ name: 'Alice', email: 'alice@mail.com' })
.expect(201);
});
});
9. CI/CD y despliegue
- Lint + Test + Build + Deploy en pipelines automáticos.
- Versionado semántico y publicación con
nx releaseosemantic-release. - Integración con Docker y Kubernetes.
- Variables seguras gestionadas por Vault o
AWS Secrets Manager. - Despliegue multi-entorno (dev/staging/prod) con configuración por módulo dinámico.
10. Beneficios y conclusiones
- Escalabilidad horizontal gracias a CQRS + Microservicios.
- Trazabilidad completa mediante Event Sourcing + Observabilidad.
- Consistencia y testabilidad por repositorios tipados e interfaces claras.
- Adaptable a cualquier dominio con mínima fricción técnica.
Relacionado
- NestJS – Avanzado y Ecosistema
- Arquitectura Hexagonal
- CQRS y Event Sourcing
- DDD
- Observabilidad
- Auth y Seguridad Backend
NestJS – Caso Práctico Completo: User + Payment Service
- NestJS
- CQRS y Event Sourcing
- Arquitectura Hexagonal
- Prisma
- Auth y Seguridad Backend
- Observabilidad
- DDD
🌐 Visión General del Microservicio
Este ejemplo muestra un microservicio realista que maneja usuarios y pagos.
Combina:
- CQRS (Command/Query separation)
- Prisma (persistencia desacoplada)
- EventBus (propagación de eventos)
- JWT Auth (seguridad modular)
- Observabilidad distribuida
El objetivo es ilustrar cómo aplicar arquitectura hexagonal + patrones DDD de forma mantenible y escalable.
🧩 Estructura de Carpetas
src/
├── domain/
│ ├── user/
│ │ ├── user.entity.ts
│ │ └── events/
│ ├── payment/
│ │ ├── payment.entity.ts
│ │ └── events/
│ └── shared/
│ └── value-objects/
│
├── application/
│ ├── user/
│ │ ├── commands/
│ │ ├── queries/
│ │ └── handlers/
│ ├── payment/
│ │ ├── commands/
│ │ ├── queries/
│ │ └── handlers/
│ └── common/
│ └── interfaces/
│
├── infrastructure/
│ ├── prisma/
│ │ └── prisma.service.ts
│ ├── repositories/
│ │ ├── user.repository.ts
│ │ └── payment.repository.ts
│ └── event-bus/
│ └── event.module.ts
│
└── interface/
├── controllers/
│ ├── user.controller.ts
│ └── payment.controller.ts
├── dto/
└── auth/
├── jwt.strategy.ts
└── auth.guard.ts
`
🧠 Dominio
Entidad User
export class User {
constructor(
private readonly id: string,
private readonly email: string,
private readonly balance: number = 0
) {}
static create(email: string): User {
const id = crypto.randomUUID();
return new User(id, email, 0);
}
increaseBalance(amount: number): User {
return new User(this.id, this.email, this.balance + amount);
}
toPrimitives() {
return { id: this.id, email: this.email, balance: this.balance };
}
}
`
Entidad Payment
export class Payment {
constructor(
private readonly id: string,
private readonly userId: string,
private readonly amount: number,
private readonly timestamp: Date
) {}
static create(userId: string, amount: number): Payment {
return new Payment(crypto.randomUUID(), userId, amount, new Date());
}
toPrimitives() {
return {
id: this.id,
userId: this.userId,
amount: this.amount,
timestamp: this.timestamp,
};
}
}
⚙️ Aplicación (CQRS)
Command: CreatePaymentCommand
export class CreatePaymentCommand {
constructor(
public readonly userId: string,
public readonly amount: number
) {}
}
CommandHandler
@CommandHandler(CreatePaymentCommand)
export class CreatePaymentHandler implements ICommandHandler<CreatePaymentCommand> {
constructor(
private readonly paymentRepo: PaymentRepository,
private readonly userRepo: UserRepository,
private readonly eventBus: EventBus
) {}
async execute(command: CreatePaymentCommand): Promise<void> {
const user = await this.userRepo.findById(command.userId);
if (!user) throw new Error('User not found');
const payment = Payment.create(user.id, command.amount);
await this.paymentRepo.save(payment);
const updated = user.increaseBalance(command.amount);
await this.userRepo.save(updated);
this.eventBus.publish(new PaymentCreatedEvent(payment.id, user.id));
}
}
Event
export class PaymentCreatedEvent {
constructor(
public readonly paymentId: string,
public readonly userId: string
) {}
}
🧱 Infraestructura
Prisma Repository (implementa interfaces)
@Injectable()
export class PrismaUserRepository implements UserRepository {
constructor(private prisma: PrismaService) {}
async findById(id: string): Promise<User | null> {
const data = await this.prisma.user.findUnique({ where: { id } });
return data ? new User(data.id, data.email, data.balance) : null;
}
async save(user: User): Promise<void> {
await this.prisma.user.upsert({
where: { id: user.toPrimitives().id },
create: user.toPrimitives(),
update: user.toPrimitives(),
});
}
}
EventBus (in-memory o Kafka)
@Module({
imports: [CqrsModule],
providers: [],
exports: [CqrsModule],
})
export class EventBusModule {}
🔒 Autenticación (JWT)
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.email };
}
}
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
🌍 Interfaz (HTTP Layer)
Controlador de Users
@ApiTags('users')
@Controller('users')
export class UserController {
constructor(private readonly userRepo: UserRepository) {}
@Post()
async create(@Body() dto: { email: string }) {
const user = User.create(dto.email);
await this.userRepo.save(user);
return user.toPrimitives();
}
@Get(':id')
async getById(@Param('id') id: string) {
const user = await this.userRepo.findById(id);
return user ? user.toPrimitives() : { error: 'Not found' };
}
}
Controlador de Payments
@ApiTags('payments')
@Controller('payments')
@UseGuards(JwtAuthGuard)
export class PaymentController {
constructor(private commandBus: CommandBus) {}
@Post()
async createPayment(@Body() dto: { userId: string; amount: number }) {
await this.commandBus.execute(new CreatePaymentCommand(dto.userId, dto.amount));
return { message: 'Payment processed' };
}
}
📊 Observabilidad
Integración con OpenTelemetry:
@Module({
imports: [
OpenTelemetryModule.forRoot({
metrics: { hostMetrics: true, apiMetrics: { enable: true } },
}),
],
})
export class ObservabilityModule {}
- Logs con
nestjs-pino:
app.useLogger(app.get(Logger));
- Endpoint
/metricspara Prometheus.
🧪 Testing de Flujo Completo
Prueba e2e del ciclo User → Payment
describe('User + Payment flow (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
it('should create a user and process payment', async () => {
const user = await request(app.getHttpServer())
.post('/users')
.send({ email: 'test@mail.com' })
.expect(201);
await request(app.getHttpServer())
.post('/payments')
.send({ userId: user.body.id, amount: 100 })
.set('Authorization', `Bearer ${process.env.JWT}`)
.expect(201);
});
});
🚀 Beneficios del Caso
- Dominio agnóstico de la infraestructura.
- Separación clara CQRS: Commands → cambios, Queries → lectura.
- Escalabilidad horizontal: microservicios desacoplados.
- EventBus extensible: posible integración con Kafka/NATS.
- Testing y trazabilidad completas.
📚 Referencias Relacionadas
- NestJS – Arquitectura Aplicada con Patrones Avanzados
- CQRS y Event Sourcing
- Arquitectura Hexagonal
- Auth y Seguridad Backend
- Observabilidad
- Prisma
- DDD
Omnivore NestJS
type: list
name: "Notas con #nestjs en Omnivore"
order:
- property: date_saved
direction: desc
columns:
- file.name
- date_saved
filters:
and:
- file.inFolder("Omnivore")
- file.hasTag("nestjs", "Nestjs")
views:
- type: table
name: Table
sort:
- property: file.mtime
direction: DESC
`
¿Te gusta este contenido? Suscríbete vía RSS