Have ideas to improve npm?Join in the discussion! »

    This package has been deprecated

    Author message:

    This package has been moved to https://github.com/nestjsplus/config

    nestjs-config-manager
    TypeScript icon, indicating that this package has built-in type declarations

    1.0.11 • Public • Published

    NestJS Configuration Manager

    Flexible, Docker-friendly, Dotenv-based Configuration Module for NestJS

    NestJS Configuration Manager Logo

    Top Features

    • Docker-friendly: e.g., use .env files in dev/test, environment variables in production, or any combination
    • @hapi/joi-based validation of environment variables
    • Completely dynamic and customizable determination of the name/location of your .env file
      • means: no code changes to handle unique configs per environment
    • Default values (i.e., optional environment variables with default values)
    • Trace how an environment variable was resolved (i.e., came from external environment, .env file or as a default value) to help debug tricky problems between dev, test, production

    Full Feature List

    Documentation

    Quick Start - Read This First

    You can read more about how NestJSConfigManager works if you want. And the simple API is documented here. But this section should get you started quickly.

    The package has one global Nest module (ConfigManagerModule), and one main class (ConfigManager) that you'll need to work with. The main idea is to create your own ConfigService (in the examples, we'll call it ConfigService, but you can call it whatever you want). You probably want this in its own module (in the examples, we'll call it ConfigModule), which you probably want to be global. You'll then provide your ConfigService for use in the rest of your application. This approach affords you a great deal of flexibility:

    • Centralized setup of the ConfigurationModule/Service
    • Use Dependency Injection to provide the service wherever needed
    • Easily override/mock the service for testing
    • Future-proof: if you later want to switch to another 3rd party config module, your dependencies are isolated in one place

    Following these conventions, your ConfigModule might look like this:

    // src/config/config.module.ts
    import { Module, Global } from '@nestjs/common';
    import { ConfigManagerModule } from 'nestjs-config-manager';
    import { ConfigService } from './config.service';
     
    @Global()
    @Module({
      imports: [
        ConfigManagerModule.register({
          useFile: 'config/development.env',
        }),
      ],
      providers: [ConfigService],
      exports: [ConfigService],
    })
    export class ConfigModule {}

    This imports and registers the ConfigManagerModule. The register() method is how you configure the module. In this example, we explicitly provide a full path to the .env file via the useFile configuration option. This is simple, but not terribly flexible. We'll explore more flexible options below. When using a static file path with useFile, the path is relative to the root directory for the project, or the root directory in which the app is running (in test and production environments). For example, if the app currently has a structure like:

    myproject
    ├── src
    │   └── module1
    ├── dist
    ├── node_modules
    ├── test
    ├── config
    |   └── development.env
    └── package.json

    The above useFile configuration method would result in the ConfigManagerModule looking for a dotenv-formatted file in:

    myproject/config/development.env

    Your ConfigService might look like this:

    // src/config/config.service.ts
    import { Injectable } from '@nestjs/common';
    import { ConfigManager } from 'nestjs-config-manager';
    import * as Joi from '@hapi/joi';
     
    @Injectable()
    export class ConfigService extends ConfigManager {
      // Our custom "schema"
      // We supply it to the ConfigManager by extending the
      // ConfigManager class and implementing the
      // provideConfigSpec() method, which simply returns
      // our custom schema
      provideConfigSpec() {
        return {
          DB_HOST: {
            validate: Joi.string(),
            required: false,
            default: 'localhost',
          },
          DB_PORT: {
            validate: Joi.number()
              .min(5000)
              .max(65535),
            required: false,
            default: 5432,
          },
          DB_USERNAME: {
            validate: Joi.string(),
            required: true,
          },
          DB_PASSWORD: {
            validate: Joi.string(),
            required: true,
          },
          DB_NAME: {
            validate: Joi.string(),
            required: true,
          },
        };
      }
    }

    Your ConfigService (you can choose any name you want) is a derived class that extends the ConfigManager class provided by the package. You must implement the provideConfigSpec() method. This is where you define your schema. Read more about schemas here.

    With this in place, you can use your ConfigService anywhere in your project. For example, assuming a .env file like:

    // myproject/config/development.env
    DB_USERNAME=john
    DB_PASSWORD=mypassword
    DB_NAME=mydb
    .
    .
    .

    A service could then use it like this:

    // src/app.service.ts
    import { Injectable } from '@nestjs/common';
    import { ConfigService } from './config/config.service';
     
    @Injectable()
    export class AppService {
      private userName: string;
     
      constructor(configService: ConfigService) {
        this.userName = configService.get<string>('DB_USERNAME'));
      }
     
      getHello(): string {
        return `Hello ${this.userName}`;
      }
    }

    And calling getHello() would return

    Hello john when run with this configuration.

    Dynamic env file location example

    Let's say you have two environments: development and test, and want to set up your ConfigService to dynamically locate the .env file that is appropriate to each environment. Let's assume that development uses one set of database credentials, and test uses another.

    This would be well represented by having two .env files. Perhaps they're stored in a folder like myproject/config (this is just an example; they can be stored wherever you want). The files might look like:

    For development:

    // myproject/config/development.env
    DB_USERNAME=devdbuser
    DB_PASSWORD=devdbpass
    DB_NAME=devdb

    For test:

    // myproject/config/test.env
    DB_USERNAME=testdbuser
    DB_PASSWORD=testdbpass
    DB_NAME=testdb

    How can we accommodate this dynamic file location without modifying our code? The useFile() method of configuration shown above won't work for this. You need a way to read a different .env file for each environment. A typical approach is to use a specific environment variable (typically NODE_ENV, though you can choose whatever you want) to indicate what the active environment is. For example, when running in development, you'd have NODE_ENV equal to development, and in test, NODE_ENV's value would equal test.

    Based on this, you can use the useEnv method of configuring the ConfigManagerModule. The useEnv method is described in the Module Configuration Options section, but a simple example is shown below.

    To accommodate this requirement, we'd modify the way we register the ConfigManagerModule as follows, replacing useFile with useEnv:

    // src/config/config.module.ts
    import { Module, Global } from '@nestjs/common';
    import { ConfigManagerModule } from 'nestjs-config-manager';
    import { ConfigService } from './config.service';
     
    @Global()
    @Module({
      imports: [
        ConfigManagerModule.register({
          useEnv: {
            folder: 'config',
          }
        }),
      ],
      providers: [ConfigService],
      exports: [ConfigService],
    })
    export class ConfigModule {}

    If instead of NODE_ENV we wanted to use an environment variable like MY_ENVIRONMENT to signify which environment we're running (e.g., MY_ENVIRONMENT is equal to development when we're in our development environment), we'd identify that environment variable using the option envKey, as shown below:

    // src/config/config.module.ts
    import { Module, Global } from '@nestjs/common';
    import { ConfigManagerModule } from 'nestjs-config-manager';
    import { ConfigService } from './config.service';
     
    @Global()
    @Module({
      imports: [
        ConfigManagerModule.register({
          envKey: 'MY_ENVIRONMENT',
          useEnv: {
            folder: 'config',
          }
        }),
      ],
      providers: [ConfigService],
      exports: [ConfigService],
    })
    export class ConfigModule {}

    Completely custom env file location

    The useEnv method provides significant flexibility, but more complex structures require an even more flexible approach. To handle arbitrarily complex environments, a third method, useFunction, is available to write custom JavaScript code to generate the appropriate path and filename dynamically. This is covered in Using a custom function.

    What's next?

    Change Log

    See Changelog for more information.

    Contributing

    Contributions welcome! See Contributing.

    Author

    • John Biundo (Y Prospect on Discord)

    License

    Licensed under the MIT License - see the LICENSE file for details.

    Install

    npm i nestjs-config-manager

    DownloadsWeekly Downloads

    14

    Version

    1.0.11

    License

    MIT

    Unpacked Size

    76.5 kB

    Total Files

    50

    Last publish

    Collaborators

    • avatar