NestJS 11: 5 Características Clave que Debes Probar esta Semana para Mejorar tu Startup Time

NestJS 11 5 Características Clave que Debes Probar esta Semana para Mejorar tu Startup Time

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

2. Release Notes de NestJS 11

3. Express 5 Documentation

4. TypeScript 5.0 Features

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.

Deja un comentario

Scroll al inicio

Discover more from Creapolis

Subscribe now to keep reading and get access to the full archive.

Continue reading