Skip to content

modular config with type casting and validation out of the box📜

License

Notifications You must be signed in to change notification settings

ukitgroup/nestjs-config

Repository files navigation

nestjs-config

Travis Coverage Status node npm

GitHub top language GitHub code size in bytes David David

license GitHub last commit semantic-release

Description

Convenient modular config for nestjs applications

  • type casting (everything in environment is string) E.g. 'true' -> boolean ...
  • runtime validation
  • compatible with typescript you will have typed configs
  • modularity - you can define configs for every module in you project
  • get configs from environment variables by design
  • convenient config definition with class-transformer/class-validator

Installation

npm install --save @ukitgroup/nestjs-config

or

yarn add @ukitgroup/nestjs-config

Short example

Override configuration for particular modules from environment

.env

CAT__WEIGHT=5

cat.config.ts

@Config('CAT')
export class CatConfig {
  @Env('WEIGHT')
  @Number()
  readonly weight: number;

  @Env('KNOWS_PROGRAMMING')
  @Boolean()
  readonly knowsProgramming: boolean = true;
}

Get the cat config in a service

cat.service.ts

@Injectable()
export class CatService {
  constructor(@Inject(CatConfig) private readonly config: CatConfig) {}

  meow(): string {
    // overridden from env
    // typeof this.config.weight === 'number'
    // this.config.weight === 5

    // default value
    // typeof this.config.knowsProgramming === 'boolean'
    // this.config.knowsProgramming === true
    return `meows..`;
  }
}

API

Define options for config in AppModule with:

ConfigModule.forRoot(options: ConfigOptions)
ConfigOptions: {
  fromFile?: string,
  configs?: ClassType[],
  imports?: NestModule[],
  providers?: Provider[],
}

If you don't set fromFile option, process.env will be used.

In addition you can provide logger to the library to log validation errors via token CONFIG_LOGGER.

So as raw object via token RAW_CONFIG. You might need it in your tests:

const moduleFixture: TestingModule = await Test.createTestingModule({
  imports: [AppModule],
})
  .overrideProvider(RAW_CONFIG)
  .useValue({
    APP__HTTP_PORT: '3000',
    CAT__WEIGHT: '5',
  })
  .compile();

Define configs in any module with:

ConfigModule.forFeature(configs: ClassType[])

Decorators

Decorator Description
Common config decorators
@Config(name: string) Add prefix to env variables
@Env(name: string) Extract env with name to this varaible
Type decorators Import from @ukitgroup/nestjs-config/types
@String() String variable (@IsString)
@Number() Number variable (parseFloat + @IsNumber
@Integer() Integer variable (parseInt + @IsInteger
@Boolean() Boolean variable ('true','false' + @IsBool`)
@Transform(transformFn: Function) Custom transformation. Import from @ukitgroup/nestjs-config/transformer
Validation decorators The same as class-validator. Import from @ukitgroup/nestjs-config/validator.

Usage

app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@ukitgroup/nestjs-config';
import { AppConfig } from './app.config';
import { CatModule } from './cat.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      fromFile: '.env',
      configs: [AppConfig],
    }),
    CatModule,
  ],
})
export class AppModule {}

Create class that describes configuration for application

app.config.ts

import { Config, Env } from '@ukitgroup/nestjs-config';
import { Integer } from '@ukitgroup/nestjs-config/types';

@Config('APP')
export class AppConfig {
  @Env('HTTP_PORT')
  @Integer()
  readonly httpPort: number = 3000;
}

Use AppConfig to configure application

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AppConfig } from './app.config';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = app.get<AppConfig>(AppConfig);

  await app.listen(config.httpPort);
}
bootstrap();

Create class that describes configuration for particular module CatsModule

cat.config.ts

import { Config, Env } from '@ukitgroup/nestjs-config';
import { Boolean, Number, String } from '@ukitgroup/nestjs-config/types';
import { Transform } from '@ukitgroup/nestjs-config/transformer';
import { IsDate, IsOptional } from '@ukitgroup/nestjs-config/validator';

@Config('CAT')
export class CatConfig {
  @Env('NAME')
  @String()
  readonly name: string;

  @Env('WEIGHT')
  @Number()
  readonly weight: number;

  @Env('KNOW_PROGRAMMING')
  @Boolean()
  readonly knowsProgramming: boolean = true;

  @Env('BIRTH_DATE')
  @Transform((value) => new Date(value))
  @IsOptional()
  @IsDate()
  readonly birthDate: Date;
}

Inject CatConfig for CatModule

cat.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@ukitgroup/nestjs-config';
import { CatConfig } from './cat.config';
import { CatsService } from './cats.service';

@Module({
  imports: [ConfigModule.forFeature([CatConfig])],
  providers: [CatsService],
})
export class CatModule {}

Get the cat config in a service

cat.service.ts

import { Inject, Injectable } from '@nestjs/common';
import { CatConfig } from './cat.config';

@Injectable()
export class CatService {
  constructor(@Inject(CatConfig) private readonly config: CatConfig) {}

  meow(): string {
    return `${this.config.name} meows..`;
  }
}

Override configuration for particular modules from environment

.env

APP__HTTP_PORT=3000
CAT__NAME=vasya
CAT__WEIGHT=5

or on launch your application

APP__HTTP_PORT=3000 CAT__NAME=vasya CAT__WEIGHT=5 node dist/main.js

Also you can see the example on github

Transformation

You can either use our built-in types like Integer, Boolean, etc..
Or transform value with your own rule with Transform:

CAT__YOUR_TYPE_VARIABLE=...
@Config('CAT')
class CatConfig {
  @Env('YOUR_TYPE_VARIABLE')
  @Transform(fn) // where fn is your transformation function
  myVariable: MyType;
}

We use our own version of class-transformer: @ukitgroup/class-transformer

If environment variable is empty or not provided, config will use default value.

Validation

CAT__WEIGHT=not_a_number
@Config('CAT')
class CatConfig {
  @Env('WEIGHT')
  @Number()
  weight: number;
}

Library will throw an error on launch application: Cat.weight received 'not_a_number' errors: {"IsNumber": "Should be number"}

Requirements

  1. @nestjs/common ^7.2.0
  2. @nestjs/core ^7.2.0

License

@ukitgroup/nestjs-config is MIT licensed.