A煤n no tienes acceso a esta clase

Crea una cuenta y contin煤a viendo este curso

Validaci贸n de esquemas en .envs con Joi

12/17
Recursos

npm install --save joi

// src/app.module.ts

import * as Joi from 'joi';  // 馃憟

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath: enviroments[process.env.NODE_ENV] || '.env',
      load: [config],
      isGlobal: true,
      validationSchema: Joi.object({ // 馃憟
        API_KEY: Joi.number().required(),
        DATABASE_NAME: Joi.string().required(),
        DATABASE_PORT: Joi.number().required(),
      }),
    }),
    ...
  ],
  ...
})
export class AppModule {}

Aportes 11

Preguntas 1

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesi贸n.

En mi vida cotidiana como developer, he perdido bastante tiempo resolviendo bugs tan tontos de este tipo, este validaci贸n deber铆a ser un must to do en todos tus proyectos. Se los recomiendo.

Genial, este tipo de configuraci贸n, esto nos ayuda es a fallar lo antes posible y as铆 evitar problemas mas serios luego cuando el c贸digo sea mas grande y tambi茅n para evitar errores mas costosos los cuales serian directamente en producci贸n 馃憤

Como dato, si importas joi as铆 import Joi from 'joi';te da errores de compilaci贸n, asi que tienes que ponerlo si o si como el profesor import * as Joi from 'joi';

El tipado en las variables de ambiente nos ayuda a solucionar errores faciles en tiempos de desarrollo.

En el momento de despliegue cuando un servidor externo nos envia variables de ambiente, tenemos que validar los tipos de datos que recibimos en nuestras variables de entorno.

Instalamos npm install joi

app.module.ts importamos joi

import * as Joi from 'joi';
...
@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath: environments[process.env.NODE_ENV] || '.env',
      load: [config],
      isGlobal: true,
      validationSchema: Joi.object({
        API_KEY: Joi.number().required(),
        DATABASE_NAME: Joi.string().required(),
        DATABASE_PORT: Joi.number().required(),
      }),
    }),
	  ...
})

Corres la aplicacion npm run start:dev

El output de errores en consola daria lo siguiente

Si la variable no se setea:

throw new Error(`Config validation error: ${error.message}`);
                ^
Error: Config validation error: "DATABASE_PORT" is required

Si la variable se seteo con otro tipo de dato:

throw new Error(`Config validation error: ${error.message}`);
                ^
Error: Config validation error: "DATABASE_PORT" must be a number

Me toc贸 hacer lo mismo pero con class-validator, dejo mi aporte por si a alguien le sirve:

.env.validation.ts

import { IsEnum, IsNumber, IsString, validateSync } from 'class-validator';
import { plainToClass } from 'class-transformer';
import { Environment } from '../src/common/models/environment.model';
class EnvironmentVariables {
  // Environment
  @IsString()
  @IsEnum(Environment)
  ENV: Environment;

  // Server config
  @IsNumber()
  PORT: number;

  @IsString()
  END_POINT: string;
}

export function validate(config: Record<string, unknown>) {
  const validatedConfig = plainToClass(EnvironmentVariables, config, {
    enableImplicitConversion: true,
  });
  const errors = validateSync(validatedConfig, {
    skipMissingProperties: false,
  });

  if (errors.length > 0) {
    throw new Error(`ENV validation error` + errors.toString());
  }
  return validatedConfig;
}
}

configuration.ts:

import { registerAs } from '@nestjs/config';

export default registerAs('config', () => {
  return {
    environment: process.env.ENV,
    server: {
      port: parseInt(process.env.PORT, 10) || 3000,
      endpoint: process.env.END_POINT,
    },
  };
});

app.module.ts

import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

import config from '../config/configuration';
import { validate } from 'config/env.validation';

import { LoggerMiddleware } from './common/middleware/appLogger.middleware';

@Module({
  imports: [
    ConfigModule.forRoot({
      load: [config],
      validate,
      isGlobal: true,
    }),
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer): void {
    consumer.apply(LoggerMiddleware).forRoutes('*');
  }
}

Me encant贸 esta secci贸n. Personalmente por el trabajo ya he tenido un poco de contacto con NestJS, pero segu铆a teniendo muchas fallas precisamente por esto, por no tener validaciones y todo mi entorno. Y esto hac铆a que mi proceso de desarrollo se vaya entorpeciendo cada vez m谩s. Muchas gracias!

Yo he sido ese devops al que le a faltado una variable de entorno durante el despliegue de una aplicaci贸n, y he pasado mucgas horas buscando el fallo en la aplicaci贸n, as铆 que apoyo la validaci贸n de las variables de entorno.

Genial las validaciones para evitar bugs!

Tambien podemos pasar las siguientes opciones de validaci贸n:

  • allowUnknown: le dice a Nestjs si permitir claves que no se encuentran definidas en nuestro esquema.

  • abortEarly: lanza una excepci贸n si hace falta una variable de entorno en nuestra configuraci贸n.

ConfigModule.forRoot({
      envFilePath: envirotments[process.env.NODE_ENV] || '.env',
      isGlobal: true,
      load: [Configuration],
      validationSchema: Joi.object({
        NODE_ENV: Joi.string().valid('dev', 'stag', 'prod').required(),
        API_KEY: Joi.string().required(),
        DB_NAME: Joi.string().required(),
        DB_PORT: Joi.number().positive().integer().required(),
      }),
      validationOptions: {
        allowUnknown: false,
        abortEarly: true,
      },
    }),

Me encanta esta feature, cada tanto siempre estos errores ocurren, me lo llevo tambi茅n para aplicar en otros lenguajes.

Excelente! Puro poder de NestJS.