Introducción
El ecosistema Node.js está en constante evolución, y NestJS se ha consolidado como uno de los frameworks más robustos para construir aplicaciones backend escalables. Con el lanzamiento de la versión 11, el equipo de NestJS ha introducido cambios significativos que no solo mejoran el rendimiento sino que también optimizan drásticamente el tiempo de inicio de aplicaciones. En este artículo, exploraremos en profundidad las 5 características más importantes que todo desarrollador debe conocer y probar inmediatamente.
Prerrequisitos
Para aprovechar al máximo las características de NestJS 11, asegúrate de tener:
– Node.js 18+ instalado
– TypeScript 5.0+
– NestJS CLI (`npm install -g @nestjs/cli`)
– Conocimientos básicos de Angular (decoradores, módulos, providers)
– Experiencia con Express.js o similar
Característica 1: Express 5 – El Nuevo Motor de Servidor
¿Qué ha cambiado?
Express.js ha sido el backbone de Node.js durante años, y la versión 5 trae consigo mejoras fundamentales que impactan directamente en el rendimiento de aplicaciones NestJS. La integración nativa con Express 5 en NestJS 11 elimina capas de abstracción innecesarias, reduciendo el overhead y mejorando los tiempos de respuesta.
Implementación Práctica
Vamos a comparar cómo se configura un servidor básico en NestJS 10 vs 11:
“`typescript
// nestjs-10.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
```
```typescript
// app.module.ts (NestJS 11 con Express 5)
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HttpAdapterHost } from '@nestjs/core';
import { expressAdapter } from '@nestjs/platform-express';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
constructor(private readonly httpAdapter: HttpAdapterHost) {}
configure() {
const adapter = this.httpAdapter.httpAdapterInstance;
// Acceso directo a la instancia de Express 5
adapter.disable('x-powered-by');
adapter.enable('trust proxy');
}
}“`
Mejores Prácticas con Express 5
“`typescript
// express-5-optimization.service.ts
import { Injectable } from '@nestjs/common';
import { Request, Response } from 'express';
@Injectable()
export class Express5OptimizationService {
optimizeResponseHeaders(res: Response) {
// Mejores encabezados de seguridad y rendimiento
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
}
compressMiddleware(req: Request, res: Response) {
// Middleware de compresión mejorado
if (req.headers['accept-encoding']?.includes('gzip')) {
res.setHeader('Content-Encoding', 'gzip');
}
}
}“`
Manejo de Errores Mejorado
“`typescript
// express-5-error-handler.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class Express5ErrorHandler implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
// Nuevo manejo de errores de Express 5
response
.status(status)
.set({
'Error-Details': JSON.stringify(exception.getResponse()),
'Request-ID': request.headers['request-id'] || 'unknown',
'Timestamp': new Date().toISOString()
})
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
method: request.method,
error: exception.message,
correlationId: request.headers['request-id']
});
}
}“`
Característica 2: Sistema de Inyección de Dependencias Optimizado
Novedades en el Sistema DI
NestJS 11 introduce cambios fundamentales en el sistema de inyección de dependencias que mejoran el tiempo de inicio en un 40-60%. La clave está en la carga perezosa y la optimización del gráfico de dependencias.
Providers Cargados de Forma Perezosa
“`typescript
// lazy-provider.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
@Injectable()
export class LazyLoadedService implements OnModuleInit {
private initialized = false;
async onModuleInit() {
// Solo se inicializa cuando se usa por primera vez
console.log('Initializing LazyLoadedService...');
this.initialized = true;
}
get data() {
if (!this.initialized) {
throw new Error('Service not initialized');
}
return { processed: true, timestamp: Date.now() };
}
}
// lazy-provider.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
@Injectable()
export class LazyLoadedService implements OnModuleInit {
private initialized = false;
async onModuleInit() {
// Solo se inicializa cuando se usa por primera vez
console.log('Initializing LazyLoadedService...');
this.initialized = true;
}
get data() {
if (!this.initialized) {
throw new Error('Service not initialized');
}
return { processed: true, timestamp: Date.now() };
}
}“`
Uso Eficiente del Decorador `@Injectable()`
“`typescript
// optimized-di.module.ts
import { Module, Injectable, Inject } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class DatabaseService {
constructor(
@Inject('CONNECTION_POOL') private connectionPool: any[],
private configService: ConfigService
) {}
get connection() {
return this.connectionPool[0];
}
}
@Module({
providers: [
{
provide: 'CONNECTION_POOL',
useFactory: async (configService: ConfigService) => {
// Solo se crea cuando se necesita
return await createConnectionPool(configService.get('DB_CONFIG'));
},
inject: [ConfigService]
},
DatabaseService
]
})
export class DatabaseModule {}“`
Ciclo de Vida Mejorado
“`typescript
// lifecycle-hooks.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy, SetMetadata } from '@nestjs/common';
export const CUSTOM_METADATA = 'customMetadata';
@Injectable()
export class LifecycleService implements OnModuleInit, OnModuleDestroy {
private metrics = {
initTime: 0,
destroyTime: 0,
memoryUsage: process.memoryUsage()
};
async onModuleInit() {
const start = Date.now();
// Lógica de inicialización pesada
await this.initializeResources();
this.metrics.initTime = Date.now() - start;
console.log(`Module initialized in ${this.metrics.initTime}ms`);
}
async onModuleDestroy() {
const start = Date.now();
// Limpieza eficiente
await this.cleanupResources();
this.metrics.destroyTime = Date.now() - start;
console.log(`Module destroyed in ${this.metrics.destroyTime}ms`);
}
private async initializeResources() {
// Simular carga pesada
await new Promise(resolve => setTimeout(resolve, 100));
}
private async cleanupResources() {
// Simular limpieza
await new Promise(resolve => setTimeout(resolve, 50));
}
getMetrics() {
return this.metrics;
}
}“`
Característica 3: ConfigService Mejorado con Validation
Validación Automática de Configuración
El nuevo ConfigService en NestJS 11 integra validación automática usando class-validator y class-transformer, eliminando la necesidad de validaciones manuales.
“`typescript
// config.dto.ts
import { IsString, IsNumber, IsBoolean, IsOptional, validateOrReject } from 'class-validator';
import { plainToClass, Transform } from 'class-transformer';
export class AppConfig {
@IsString()
@IsOptional()
NODE_ENV: string = 'development';
@IsNumber()
@Transform(({ value }) => Number(value))
PORT: number = 3000;
@IsString()
DATABASE_URL: string;
@IsBoolean()
@Transform(({ value }) => value === 'true')
ENABLE_CACHING: boolean = false;
@IsString({ each: true })
ALLOWED_ORIGINS: string[] = [];
}
@Injectable()
export class CustomConfigService {
private config: AppConfig;
constructor() {
this.config = this.loadConfig();
}
private loadConfig(): AppConfig {
const rawConfig = {
NODE_ENV: process.env.NODE_ENV,
PORT: process.env.PORT,
DATABASE_URL: process.env.DATABASE_URL,
ENABLE_CACHING: process.env.ENABLE_CACHING,
ALLOWED_ORIGINS: process.env.ALLOWED_ORIGINS?.split(',') || []
};
// Transforma y valida automáticamente
const config = plainToClass(AppConfig, rawConfig);
return config;
}
async validate() {
try {
await validateOrReject(this.config);
console.log('Configuration is valid');
} catch (errors) {
console.error('Configuration validation failed:', errors);
throw new Error('Invalid configuration');
}
}
get<T extends keyof AppConfig>(key: T): AppConfig[T] {
return this.config[key];
}
}“`
Configuración Jerárquica
“`typescript
// hierarchical-config.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [
() => ({
database: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
},
cache: {
provider: process.env.CACHE_PROVIDER || 'redis',
ttl: parseInt(process.env.CACHE_TTL || '300'),
}
})
]
})
]
})
export class AppModule {}
// service.ts
@Injectable()
export class AppService {
constructor(private configService: ConfigService) {}
getDatabaseConfig() {
return this.configService.get('database');
}
getCacheConfig() {
return this.configService.get('cache');
}
}“`
Manejo de Secrets de Forma Segura
“`typescript
// secrets-manager.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class SecretsManagerService implements OnModuleInit {
private secrets: Map<string, string> = new Map();
constructor(private configService: ConfigService) {}
async onModuleInit() {
await this.loadSecrets();
}
private async loadSecrets() {
// Carga secrets desde múltiples fuentes con prioridad
const secretProviders = [
() => this.configService.get('secrets'),
() => process.env.SECRETS_JSON,
() => this.loadFromVault()
];
for (const provider of secretProviders) {
try {
const secrets = await provider();
if (secrets) {
Object.entries(secrets).forEach(([key, value]) => {
this.secrets.set(key, value as string);
});
break; // Usar el primer proveedor exitoso
}
} catch (error) {
console.warn(`Secret provider failed: ${error.message}`);
}
}
}
getSecret(key: string): string | undefined {
return this.secrets.get(key);
}
private async loadFromVault(): Promise<any> {
// Implementación de Vault client
// return await vaultClient.getSecrets();
return {};
}
}“`
Característica 4: Testing Simplificado y Mejorado
Test de Integración con Jest 29
NestJS 11 incluye soporte mejorado para Jest 29 con configuraciones predefinidas y herramientas de testing más potentes.
“`typescript
// app.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { AppService } from './app.service';
import { ConfigService } from '@nestjs/config';
describe('AppService', () => {
let service: AppService;
let configService: ConfigService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
AppService,
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
const mockConfig = {
'app.name': 'TestApp',
'app.version': '1.0.0',
'database.url': 'test-db-url'
};
return mockConfig[key as keyof typeof mockConfig];
})
}
}
]
}).compile();
service = module.get<AppService>(AppService);
configService = module.get<ConfigService>(ConfigService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should return app info', () => {
const result = service.getInfo();
expect(result).toEqual({
name: 'TestApp',
version: '1.0.0'
});
});
it('should throw error when config is missing', () => {
jest.spyOn(configService, 'get').mockReturnValueOnce(undefined);
expect(() => service.getInfo()).toThrow();
});
});“`
Test de Endpoints con Supertest
“`typescript
// app.e2e-spec.ts
import { Test } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule]
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
describe('GET /', () => {
it('should return 200 and welcome message', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect({
message: 'Welcome to NestJS 11!'
});
});
});
describe('GET /health', () => {
it('should return health check', async () => {
const response = await request(app.getHttpServer())
.get('/health')
.expect(200);
expect(response.body).toHaveProperty('status', 'ok');
expect(response.body).toHaveProperty('timestamp');
expect(response.body).toHaveProperty('uptime');
});
});
});“`
Test con Coverage Mejorado
“`typescript
// coverage-test.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class CoverageTestService {
publicMethod(input: string): string {
return `Hello ${input}`;
}
privateMethod(input: string): string {
return `Private ${input}`;
}
protectedMethod(input: string): string {
return `Protected ${input}`;
}
// Método complejo para testing
calculateComplexValue(a: number, b: number): number {
if (a < 0) throw new Error('Negative value not allowed');
if (b === 0) return a;
const result = a * b + Math.sqrt(a);
return Math.round(result * 100) / 100;
}
}
```
```typescript
// coverage-test.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CoverageTestService } from './coverage-test.service';
describe('CoverageTestService', () => {
let service: CoverageTestService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [CoverageTestService]
}).compile();
service = module.get<CoverageTestService>(CoverageTestService);
});
describe('publicMethod', () => {
it('should return greeting with input', () => {
const result = service.publicMethod('World');
expect(result).toBe('Hello World');
});
});
describe('calculateComplexValue', () => {
it('should calculate complex value correctly', () => {
const result = service.calculateComplexValue(4, 2);
expect(result).toBe(9); // 4 * 2 + sqrt(4) = 8 + 2 = 10
});
it('should throw error for negative values', () => {
expect(() => service.calculateComplexValue(-1, 2)).toThrow('Negative value not allowed');
});
it('should return original value when b is 0', () => {
const result = service.calculateComplexValue(5, 0);
expect(result).toBe(5);
});
});
});“`
Característica 5: CLI Actualizado y Mejorado
Nuevos Comandos y Flags
NestJS 11 introduce comandos CLI mejorados con más opciones y mejor rendimiento.
“`bash
# Generar un módulo con lazy loading
nest g module user --lazy
# Crear un servicio con dependencias inyectadas
nest g service user --no-spec
# Generar un guard con configuración
nest g guard auth --implements CanActivate
# Crear un interceptor de logging
nest g interceptor logging --implements NestInterceptor
# Generar un pipe de validación
nest g pipe validation --implements PipeTransform“`
### Configuración Personalizada del CLI
“`json
// nest-cli.json
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true,
"watchAssets": true,
"skipLibCheck": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"plugins": [
{
"name": "@nestjs/swagger",
"options": {
"classValidatorShim": true,
"introspectComments": true
}
}
]
},
"monorepo": true,
"root": "apps/api",
"projects": {
"api": {
"type": "application",
"root": "apps/api",
"entryFile": "main",
"sourceRoot": "apps/api/src",
"compilerOptions": {
"tsConfigPath": "apps/api/tsconfig.app.json"
}
}
}
}“`
Scripts de Build Optimizados
“`json
// package.json
{
"scripts": {
"build": "nest build",
"build:watch": "nest build --watch",
"build:debug": "nest build --debug",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint . --ext .ts,.js",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"typeorm": "nest start --exec ts-node -p src/main.ts",
"migration:create": "typeorm migration:create -d src/database/migrations",
"migration:generate": "typeorm migration:generate -d src/database/migrations",
"migration:run": "typeorm migration:run -d src/database/migrations"
}
}“`
Generación de Código con Plantillas Personalizadas
“`typescript
// custom-schematic.ts
import {
apply,
url,
move,
template,
mergeWith,
branchAndMerge,
SchematicsException,
Tree,
Rule
} from '@angular-devkit/schematics';
import { strings } from '@angular-devkit/core';
import { parseName } from '@schematics/angular/utility/parse-name';
export function generateCustomFeature(options: any): Rule {
return (host: Tree) => {
const path = options.path;
if (!path) {
throw new SchematicsException('path option is required');
}
const parsedPath = parseName(path, options.name);
options.name = parsedPath.name;
options.path = parsedPath.path;
const templateSource = apply(url('./files'), [
template({
...strings,
'dot': '.',
...options
}),
move(parsedPath.path)
]);
return mergeWith(templateSource);
};
}“`
Preguntas Frecuentes (FAQ)
1. ¿NestJS 11 es compatible con Node.js 16?
Respuesta: No, NestJS 11 requiere Node.js 18+ debido a las dependencias actualizadas y las nuevas características del runtime. Se recomienda usar la versión LTS más reciente para obtener mejor rendimiento y soporte de seguridad.
2. ¿Cómo migrar de NestJS 10 a 11?
Respuesta: La migración involves principalmente actualizar las dependencias y adaptar los decoradores. Ejecuta:
“`bash
npm install @nestjs/common@latest @nestjs/core@latest
npm install express@^5.0.0“`
3. ¿Las nuevas características mejoran el rendimiento en producción?
Respuesta: Sí, los benchmarks muestran mejoras del 30-50% en tiempo de inicio y 20-30% en throughput debido a la optimización del sistema DI y la integración con Express 5.
4. ¿Puedo usar NestJS 11 con microservicios?
Respuesta: Absolutamente. NestJS 11 incluye mejoras específicas para arquitecturas de microservicios con mejor manejo de conexiones y balanceo de carga.
5. ¿Cómo afecta el nuevo ConfigService a las pruebas?
Respuesta: El nuevo ConfigService facilita las pruebas mediante inyección de dependencias y mock de configuraciones sin necesidad de variables de entorno complejas.
6. ¿Son las nuevas características de CLI compatibles con proyectos monorepo?
Respuesta: Sí, el CLI actualizado incluye soporte mejorado para monorepos con manejo de múltiples aplicaciones y librerías compartidas.
7. ¿Vale la pena actualizar proyectos existentes a NestJS 11?
Respuesta: Sí, especialmente si priorizas rendimiento y productividad. Las mejoras en testing y CLI reducen significativamente el tiempo de desarrollo.
Puntos Clave para Recordar
1. Express 5 integration mejora drásticamente el rendimiento con menos overhead
2. Sistema DI optimizado reduce tiempos de inicio hasta 60%
3. ConfigService mejorado con validación automática y manejo de secrets
4. Testing simplificado con soporte para Jest 29 y mejor coverage
5. CLI actualizado con comandos más eficientes y plantillas personalizables
Conclusión
NestJS 11 representa un salto significativo en el ecosistema Node.js, combinando rendimiento mejorado con herramientas de desarrollador más potentes. Las cinco características exploradas en este artículo no solo mejoran el rendimiento de tus aplicaciones sino que también optimizan drásticamente tu flujo de trabajo. La inversión en adoptar estas nuevas características se amortiza rápidamente en tiempos de desarrollo más rápidos y aplicaciones más eficientes.
La próxima vez que inicies un nuevo proyecto NestJS o actualices uno existente, asegúrate de aprovechar estas características para construir aplicaciones más rápidas, seguras y mantenibles.
Recursos Adicionales
1. Documentación Oficial de NestJS 11
5. [Jest 29 Documentation](https://jestjs.io/docs/getting-started)
Path de Aprendizaje Recomendado
1. Semana 1: Configuración básica y Express 5
2. Semana 2: Sistema de inyección de dependencias
3. Semana 3: ConfigService y validación
4. Semana 4: Testing mejorado
5. Semana 5: CLI y herramientas avanzadas
Desafío para Practicar
Crea una aplicación NestJS 11 que implemente todas las características aprendidas:
1. Configura un servidor con Express 5 y middlewares optimizados
2. Implementa un sistema DI con providers lazy-loaded
3. Crea un ConfigService con validación de entorno
4. Escribe tests unitarios y e2e con cobertura al 90%
5. Usa el CLI para generar código automáticamente
Comparte tus resultados en la comunidad NestJS para recibir feedback de otros desarrolladores.


