Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nestjsx/nestjs-config
Config module for nestjs using dotenv :key:
https://github.com/nestjsx/nestjs-config
config dotenv nestjs typescript
Last synced: 27 days ago
JSON representation
Config module for nestjs using dotenv :key:
- Host: GitHub
- URL: https://github.com/nestjsx/nestjs-config
- Owner: nestjsx
- License: mit
- Created: 2018-02-24T14:56:57.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2023-08-11T10:26:55.000Z (over 1 year ago)
- Last Synced: 2024-05-22T15:21:00.799Z (6 months ago)
- Topics: config, dotenv, nestjs, typescript
- Language: TypeScript
- Homepage:
- Size: 2.19 MB
- Stars: 695
- Watchers: 7
- Forks: 44
- Open Issues: 31
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
- awesome-nestjs - Nestjs Config - 处理项目配置的一个很棒的模块 (资源 / 组件和库)
README
Nestjs Config
Configuration component for NestJs.
## Features
- Load your configuration files using globs
- Support for different environment configurations, thanks to [dotenv](https://github.com/motdotla/dotenv)
- Change and Load configuration at runtime### Installation
**Yarn**
```bash
yarn add nestjs-config
```**NPM**
```bash
npm install nestjs-config --save
```### Getting Started
Let's imagine that we have a folder called `src/config` in our project that contains several configuration files.
```bash
/src
├── app.module.ts
├── config
│ ├── express.ts
│ ├── graphql.ts
│ └── grpc.ts
```Let's register the config module in `app.module.ts`
```ts
import { Module } from '@nestjs/common';
import { ConfigModule } from 'nestjs-config';
import * as path from 'path';@Module({
imports: [
ConfigModule.load(path.resolve(__dirname, 'config', '**/!(*.d).{ts,js}')),
],
})
export class AppModule {}
```
That's it!-----
#### Complex Project Structure
Now let's say that your application isn't located in a folder called `src`, but it's located in `src/app`.
We want to be able to set a different 'root path' to load our configurations from. Be it `src` or `dist`.
Imagine a more complex project structure like this:
```
/
├── dist/
├── src/
│ ├── app/
│ │ ├── app.module.ts
│ │ └── bootstrap/
│ │ │ ├── index.ts
│ │ │ └── bootstrap.module.ts
│ ├── migrations/
│ ├── cli/
│ ├── config/
│ │ ├── app.ts
│ │ └── database.ts
│ └── main.ts
├── tsconfig.json
└── package.json
```In this example, config files are located in the `/src/config` folder, because they are shared
between app, migrations and cli scripts.Also during typescript compilation all files from `src/` folder will be moved to the `dist/` folder.
Moreover, the `ConfigModule` is imported in the `BootstrapModule`, but not directly in `AppModule`.
```ts
// app.module.ts
import { Module } from '@nestjs/common';
import { BootstrapModule } from './bootstrap';
import { ConfigService } from 'nestjs-config';ConfigService.rootPath = path.resolve(__dirname, '..');
@Module({
imports: [BootstrapModule],
})
export class AppModule {}
``````typescript
// bootstrap.module.ts
import * as path from 'path';
import { Module } from '@nestjs/common';
import { ConfigModule } from 'nestjs-config';@Module({
imports: [
ConfigModule.load(path.resolve('config', '**/!(*.d).{ts,js}')),
],
})
export class BootstrapModule {}
```
Setting the `ConfigService.rootPath` before calling `ConfigModule.load(...)` will change the default root dir of where your configs are loaded from.Another method is to invoke `ConfigModule.resolveRootPath(__dirname)` from any module before loading the config and use glob with a relative path.
```ts
// bootstrap.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from 'nestjs-config';
@Module({
imports: [
ConfigModule.resolveRootPath(__dirname).load('config/**/!(*.d).{ts,js}')
],
})
export class BootstrapModule {}
```In both cases we provide the glob of our configuration as first argument, but it is relative to the `src/` folder (or eventually `dist/`).
## Multi-modular config usage
In some cases your structure might take on this shape
```
/
├── src/
│ ├── cats/
│ │ ├── cats.module.ts
│ │ └── cats.config.ts
│ ├── dogs/
│ │ ├── dogs.module.ts
│ │ └── dogs.config.ts
│ ├── app.module.ts
│ └── main.ts
├── tsconfig.json
└── package.json
```With the examples above you'd have to call your config like so `ConfigService.get('dogs.config.bark')`. You can use the `modifyConfigName` method option to change the name of your configs
```ts
import { Module } from '@nestjs/common';
import { ConfigModule } from 'nestjs-config';
import * as path from 'path';@Module({
imports: [
ConfigModule.load(path.resolve(__dirname, '**/!(*.d).config.{ts,js}'), {
modifyConfigName: name => name.replace('.config', ''),
}),
],
})
export class AppModule {}
```
Now you can call your config like so `ConfigService.get('dogs.bark')`.## Production environments
You might have notice the use of `config/**/!(*.d).{ts,js}` in the glob. When running in production (running in JavaScript after TypeScript compilation) we want to disinclude the TypeScript definition files. The use of `config/**/*.ts` is fine in dev environments but we recommend using this example `config/**/!(*.d).{ts,js}` to avoid issues later on when running in a production environment.
## Environment Configuration
This package ships with the amazing [dotenv](https://github.com/motdotla/dotenv) package that allows you to create
a `.env` file in your preferred location.Let's create one, for demo purposes!
```bash
# .env
EXPRESS_PORT=3000
```Now, in our `src/config/express.ts` configuration file, we can refer to that environment variable.
```ts
// src/config/express.ts
export default {
port: process.env.EXPRESS_PORT || 3000,
}
```> **Note:** By default the package look for a `.env` file in the path that you have started your server from.
If you want to specify another path for your `.env` file, use the second parameter of `ConfigModule.load()`.### Usage
Now we are ready to inject our `ConfigService` anywhere we'd like.
```ts
import {ConfigService} from 'nestjs-config';@Injectable()
class SomeService {constructor(private readonly config: ConfigService) {
this.config = config;
}
isProduction() {
const env = this.config.get('app.environment');
return env === 'production';
}
}
```You may also use the `@InjectConfig` decorator as following:
```ts
import {InjectConfig} from 'nestjs-config';@Injectable()
class SomeService {constructor(@InjectConfig() private readonly config) {
this.config = config;
}
}
```## Custom Helpers
This feature allows you to create small helper function that computes values from your configurations.Reconsider the `isProduction()` method from above. But in this case, let's define it as a helper:
```ts
// src/config/express.tsexport default {
environment: process.env.EXPRESS_ENVIRONMENT,
port: process.env.EXPRESS_PORT,
// helpers
isProduction() {
return this.get('express.environment') === 'production';
}
}
```You can use the helper function as follows:
```ts
// this.config is the ConfigService!
this.config.get('express').isProduction();// or
this.config._isProduction(); // note the underscore prefix.
```#### Global Helpers
You can also attach helpers to the global instance as follow:
```ts
this.config.registerHelper('isProduction', () => {
return this.get('express.environment') === 'production';
});
```And then use it like this:
```ts
this.config.isProduction(); // note the missing underscore prefix
```## Decorators
It's possible to use decorators instead of injecting the `ConfigService`.
Note that the `@Configurable()` decorator replaces the `descriptor.value` for the
method with its own function. Regarding to the current nestjs implementation
([Issue-1180](https://github.com/nestjs/nest/issues/1180)), this behavior will
break all decorators that **FOLLOW AFTER** the `@Configurable()` decorator.For the expected behavior, the `@Configurable()` decorator **MUST** be placed at
the last position for one method.**Working Example:**
```ts
import {Injectable, Get} from '@nestjs/common';
import {Configurable, ConfigParam} from 'nestjs-config';@Injectable()
export default class UserController {
@Get('/')
@Configurable()
index(@ConfigParam('my.parameter', 'default value') parameter?: string) {
return { data: parameter };
}
}
```**Broken Example:**
```ts
import {Injectable, Get, UseInterceptors} from '@nestjs/common';
import {Configurable, ConfigParam} from 'nestjs-config';
import {TransformInterceptor} from '../interceptors';@Injectable()
export default class UserController {
@Configurable()
@Get('/') // <-- nestjs decorator won't work because it placed after @Configurable()
@UseInterceptors(TransformInterceptor)// <-- nestjs decorator won't work because it placed after @Configurable()
index(@ConfigParam('my.parameter', 'default value') parameter?: string) {
return { data: parameter };
}
}
```**Broken Example 2:**
```ts
import {Injectable, Get, UseInterceptors} from '@nestjs/common';
import {Configurable, ConfigParam} from 'nestjs-config';
import {TransformInterceptor} from '../interceptors';@Injectable()
export default class UserController {
@Get('/') // <-- nestjs decorator will work fine because it placed before @Configurable()
@Configurable()
@UseInterceptors(TransformInterceptor) // <-- nestjs decorator won't work because it placed after @Configurable()
index(@ConfigParam('my.parameter', 'default value') parameter?: string) {
return { data: parameter };
}
}
```## TypeORM
Using the `ConfigModule` in combination with [TypeORM](https://github.com/typeorm/typeorm) (e.g. in order to configure TypeORM) requires using the `forRootAsync()` function supplied by the typeorm package for nestjs (`@nestjs/typeorm`)
```ts
import {Module} from '@nestjs/common';
import {ConfigModule, ConfigService} from 'nestjs-config';
import {TypeOrmModule} from '@nestjs/typeorm';
import * as path from 'path';@Module({
imports: [
ConfigModule.load(path.resolve(__dirname, 'config', '**', '!(*.d).{ts,js}')),
TypeOrmModule.forRootAsync({
useFactory: (config: ConfigService) => config.get('database'),
inject: [ConfigService],
}),
],
})
export default class AppModule {}
```Your config file may look something like this:
```ts
//config/database.ts
export default {
type: 'mysql',
host: process.env.TYPEORM_HOST,
username: process.env.TYPEORM_USERNAME,
password: process.env.TYPEORM_PASSWORD,
database: process.env.TYPEORM_DATABASE,
port: parseInt(process.env.TYPEORM_PORT),
logging: process.env.TYPEORM_LOGGING === 'true',
entities: process.env.TYPEORM_ENTITIES.split(','),
migrationsRun: process.env.TYPEORM_MIGRATIONS_RUN === 'true',
synchronize: process.env.TYPEORM_SYNCHRONIZE === 'true',
};
```> We recommend using a `TYPEORM_` prefix so when running in production environments you're also able to use the same envs for running the typeorm cli. [More options here](http://typeorm.io/#/using-ormconfig/using-environment-variables)
## Custom env file path
You can specify dotenv options with the second parameter of the load method
```ts
ConfigModule.load(path.resolve(__dirname, '*/**!(*.d).config.{ts,js}'), {
path: path.resolve(__dirname, '..', '.env.staging')),
});
```## Using different env files
Create a `.env` file at the root directory of your application had always been the best practice.
You could also create custom .env file per different environments.```bash
# .env
EXPRESS_PORT=3000# .env.dev
EXPRESS_PORT=3001# .env.testing
EXPRESS_PORT=3002# .env.staging
EXPRESS_PORT=3003
``````ts
const ENV = process.env.NODE_ENV;ConfigModule.load(path.resolve(__dirname, '*/**!(*.d).config.{ts,js}'), {
path: path.resolve(process.cwd(), !ENV ? '.env' : `.env.${ENV}`),
});
```Perhaps you need a custom directory to manage env files.
In this example, the name of the custom directory is `env`.
```bash
/
├── dist/
├── env/
│ ├── .env.dev
│ ├── .env.testing
│ └── .env.staging
│ │
├── src/
│ ├── config/
│ │ └── typeorm.config.ts
│ └── main.ts
├── tsconfig.json
└── package.json
``````ts
const ENV = process.env.NODE_ENV;ConfigModule.load(path.resolve(__dirname, '*/**!(*.d).config.{ts,js}'), {
path: path.resolve(process.cwd(), 'env', !ENV ? '.env' : `.env.${ENV}`),
});
```> **Note:** If you place env files inside an `src` directory, you won't be able to see env files included as a final output in `outDir` since TS compiler will never transpile files that do not match with \*.ts extension.
## Support
Any support is welcome. At least you can give us a star :star:
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
## ConfigService API
#### get(param: string | string[], value: any = undefined): any
Get a configuration value via path, you can use `dot notation` to traverse nested object. It returns a default value if the key does not exist.```ts
this.config.get('server.port'); // 3000
this.config.get('an.undefined.value', 'foobar'); // 'foobar' is returned if the key does not exist
```#### set(param: string | string[], value: any = null): Config
Set a value at runtime, it creates the specified key / value if it doesn't already exists.```ts
this.config.set('server.port', 2000); // {server:{ port: 2000 }}
```#### has(param: string | string[]): boolean
Determine if the given path for a configuration exists and is set.```ts
this.config.has('server.port'); // true or false
```#### merge(glob: string, options?: DotenvOptions): Promise
Load other configuration files at runtime. This is great for package development.```ts
@Module({})
export class PackageModule implements NestModule {constructor(@InjectConfig() private readonly config) {}
async configure(consumer: MiddlewareConsumer) {
await this.config.merge(path.resolve(__dirname, '**/!(*.d).{ts,js}'));
}
}
```#### registerHelper(name: string, fn: (...args:any[]) => any): ConfigService
Register a custom global helper function```ts
this.config.registerHelper('isProduction', () => {
return this.get('express.environment') === 'production';
});
```#### resolveRootPath(path: string): typeof ConfigService
change the root path from where configs files are loaded```ts
import { Module } from '@nestjs/common';
import { ConfigModule } from 'nestjs-config';@Module({
imports: [
ConfigModule.resolveRootPath(__dirname).load(path.resolve(__dirname, '**/!(*.d).{ts,js}')),
],
})
export class AppModule {}
```#### root(path: string = ''): string
Returns the current working dir or defined rootPath.
```ts
ConfigService.root(); // /var/www/src
ConfigService.root('some/path/file.html'); // /var/www/src/some/path/file.htmlConfigService.resolveRootPath(__dirname).root(); // /var/www/src/app (or wherever resolveRootPath has been called with)
```-----