En esta página
Configuración y gestión de entornos
La importancia de una buena gestión de configuración
En una aplicación NestJS, la configuración incluye todo aquello que varía entre entornos: credenciales de base de datos, secrets de JWT, URLs de servicios externos, puertos, timeouts, límites de tasa, etc. Gestionar esto correctamente es fundamental para:
- Seguridad: Los secrets no deben estar en el código fuente
- Flexibilidad: La misma imagen Docker funciona en development, staging y production
- Fiabilidad: Si falta una variable crítica, la aplicación debe fallar al arrancar, no durante la ejecución
Instalación
npm install @nestjs/config @hapi/joi
# O si prefieres la validación con Zod:
npm install @nestjs/config zodEl módulo ConfigModule básico
En su forma más simple, ConfigModule.forRoot() carga el archivo .env y hace las variables disponibles en process.env:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // no necesitas importarlo en cada módulo
}),
],
})
export class AppModule {}Archivos .env por entorno
Puedes tener múltiples archivos .env para diferentes entornos:
.env # valores por defecto o development
.env.development # solo development (sobreescribe .env)
.env.production # solo producción
.env.test # solo testing
.env.local # local del desarrollador (no commitear)En ConfigModule.forRoot, especifica el orden de carga:
ConfigModule.forRoot({
envFilePath: [
`.env.${process.env['NODE_ENV'] ?? 'development'}`,
'.env',
],
})Los archivos se cargan en orden y el primero tiene prioridad.
Usando ConfigService
ConfigService es el servicio inyectable que proporciona acceso tipado a las variables:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppService {
constructor(private readonly config: ConfigService) {}
getInfo() {
return {
nombre: 'Mi API',
entorno: this.config.get<string>('NODE_ENV', 'development'),
puerto: this.config.get<number>('PORT', 3000),
};
}
}Validación con Zod (alternativa moderna a Joi)
Si prefieres Zod para la validación:
// config/env.schema.ts
import { z } from 'zod';
export const EnvSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.coerce.number().default(3000),
DB_HOST: z.string().min(1, 'DB_HOST es requerido'),
DB_PORT: z.coerce.number().default(5432),
DB_USER: z.string().min(1),
DB_PASS: z.string().min(1),
DB_NAME: z.string().min(1),
JWT_SECRET: z.string().min(32, 'JWT_SECRET debe tener al menos 32 caracteres'),
JWT_EXPIRES_IN: z.string().default('15m'),
FRONTEND_URL: z.string().url('FRONTEND_URL debe ser una URL válida'),
});
export type Env = z.infer<typeof EnvSchema>;
// En main.ts — validar antes de arrancar
import { EnvSchema } from './config/env.schema';
async function bootstrap() {
const resultado = EnvSchema.safeParse(process.env);
if (!resultado.success) {
console.error('❌ Variables de entorno inválidas:');
console.error(resultado.error.flatten().fieldErrors);
process.exit(1);
}
const app = await NestFactory.create(AppModule);
await app.listen(resultado.data.PORT);
}Configuración de TypeORM usando ConfigService
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => {
const isProduction = config.get('NODE_ENV') === 'production';
return {
type: 'postgres',
host: config.getOrThrow('DB_HOST'),
port: config.getOrThrow<number>('DB_PORT'),
username: config.getOrThrow('DB_USER'),
password: config.getOrThrow('DB_PASS'),
database: config.getOrThrow('DB_NAME'),
autoLoadEntities: true,
synchronize: !isProduction,
logging: !isProduction,
ssl: isProduction ? { rejectUnauthorized: false } : false,
extra: isProduction
? {
max: 20, // máximo de conexiones en el pool
connectionTimeoutMillis: 5000,
}
: {},
};
},
})Variables de entorno en el archivo .env de ejemplo
Crea un archivo .env.example (que SÍ se commitea) para documentar las variables requeridas:
# .env.example — copia a .env y rellena los valores reales
# Aplicación
NODE_ENV=development
PORT=3000
FRONTEND_URL=http://localhost:4200
# Base de datos PostgreSQL
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASS=tu_password_aqui
DB_NAME=mi_api_dev
DB_SSL=false
# JWT — genera con: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
JWT_SECRET=CAMBIAR_POR_SECRET_REAL_DE_AL_MENOS_64_CARACTERES
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=CAMBIAR_POR_SECRET_DIFERENTE_PARA_REFRESH
JWT_REFRESH_EXPIRES_IN=7d
# Redis (opcional)
REDIS_HOST=localhost
REDIS_PORT=6379Acceder a la configuración en main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = app.get(ConfigService);
const puerto = config.getOrThrow<number>('PORT');
const entorno = config.get('NODE_ENV', 'development');
// Configurar CORS basado en el entorno
if (entorno !== 'production') {
app.enableCors({ origin: true });
} else {
app.enableCors({
origin: config.getOrThrow('FRONTEND_URL'),
credentials: true,
});
}
await app.listen(puerto);
console.log(`Aplicación corriendo en el puerto ${puerto} (${entorno})`);
}Una configuración robusta es la base de una aplicación que funciona correctamente en todos los entornos. En la próxima lección aprenderemos a documentar nuestra API automáticamente con Swagger, generando documentación interactiva directamente desde los decoradores de NestJS.
Inicia sesión para guardar tu progreso