# nestjs-config loads typed config files into a NestJS module

> nestjs-config is a NestJS configuration module that loads config files matched by globs, supports per environment dotenv files, and can reload configuration at runtime, with options for the root path and for renaming config keys. It suits a Nest application that wants config as TypeScript modules rather than as one large environment variable, and its release cadence has been slow.

**nestjsx/nestjs-config** — Config module for nestjs using dotenv :key:

- Repository: https://github.com/nestjsx/nestjs-config
- Stars: 704 · Forks: 42
- Language: TypeScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/nestjsx-nestjs-config

## Config as modules, not as one file of variables

nestjs-config is a config module for NestJS using dotenv, and its feature list is short enough to state plainly. It loads your configuration files using globs, it supports different environment configurations thanks to dotenv, and it can change and load configuration at runtime. The distinction from the built-in approach is what the glob buys you. A Nest application typically has one configuration module, one file, and a set of keys. This module lets the configuration live in several files, discovered by pattern, and each file is an ordinary module whose default export is a plain object. The example in the README shows a src/config folder holding express.ts, graphql.ts and grpc.ts, registered in the app module with a single import:

```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 is the whole setup in a Nest project. The glob is the important part, and the exclusion of the .d pattern is deliberate rather than defensive, since the production section explains it as a way to avoid loading TypeScript definition files once the code has been compiled to JavaScript.

## Root path handling is the part every real project hits

The complex project structure section exists because a real Nest application is not shaped like the tutorial. If the application lives in src/app rather than src, and TypeScript compilation moves everything from src/ to dist/, then a path resolved relative to the module file will point at the wrong place. The module offers two answers. The first is to set ConfigService.rootPath before calling ConfigModule.load, which changes the default root directory the configs are loaded from:

```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 {}
```

The second is to call ConfigModule.resolveRootPath(__dirname) from any module before loading the config, and then use a relative glob, so the pattern is relative to src/ or eventually dist/. The example shows that call chained into the load, with the same glob the other example uses. In both cases the glob of the configuration is given as the first argument but is relative to the src folder, or eventually the dist folder, which is the detail that makes the two layouts behave the same.

## Config naming can be normalised with one option

The multi-modular configuration case is the one where a directory holds one module and one config file per feature, with a cats module and a cats.config.ts, a dogs module and a dogs.config.ts, and an app module and a main file. The default consequence of that layout is that a config lookup carries the file name, so you would call ConfigService.get('dogs.config.bark'). The fix is the modifyConfigName method option, which rewrites the name of your configs as they are registered:

```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 {}
```

After that, the same value is reached with ConfigService.get('dogs.bark'). The option is a function, so it can do more than strip a suffix, and the pattern it applies to also has to change, since the glob now matches files ending in .config.ts or .config.js rather than every file in the tree. That coupling between the glob and the rename is the part to get right, because a glob that still matches unrelated files will try to load them as configuration.

## dotenv handles the environment layer

The package ships with dotenv, which allows you to create a .env file in your preferred location, and the README shows a minimal one with a single port variable. The configuration file then reads that value from the environment, with the environment variable taking precedence over the fallback:

```ts
// src/config/express.ts
export default {
    port: process.env.EXPRESS_PORT || 3000,
}
```

The design is that the .env file is the environment specific layer and the TypeScript config files are the structural layer, and a config module composes them. The note in the README says that by default the package looks for a .env file in the application root, and the section continues beyond what is shown here. The dependency list in the manifest backs the whole approach up, with dotenv for the environment files and glob for the file discovery, plus lodash.get and lodash.set, which are how a dotted key such as dogs.bark is read from the nested object and how it is written back at runtime. That pair is what makes the runtime reload possible, since setting a nested value in place is otherwise awkward in plain JavaScript.

## Versions, peer range and how the package is built

The maintenance picture is the thing to weigh before adopting this. The recent releases are v1.4.5 from 2019-12-17, v1.4.4 from 2019-08-29 and v2.0.0-beta from 2019-07-31, and the version in the package manifest is 1.4.11. So the last tagged release is from the end of 2019, while the repository is not archived and the last push was on 2026-10-19, which means work has continued on the branch without new tags. The peer dependency is @nestjs/common, with a range running from ^5.1.0 through ^6.0.3, ^7.0.1, ^8.0.0, ^9.0.0 and ^10.0.0, so the package is declared compatible with a wide span of Nest majors even though the last release predates several of them. The build is a plain TypeScript compile, with the build script removing the dist directory, running tsc and then building the index, and the index build running tsc with declarations and skipLibCheck against the root index.ts. Tests are jest with ts-jest, rooted in src with the spec file pattern, and the repository carries examples/ with basic, decorators and typeorm directories, which are the three ways the module is meant to be used.

## Conclusion

Adopt nestjs-config when your Nest application wants configuration expressed as typed TypeScript modules, split across several files, and you want to change values at runtime without a restart. Do not adopt it as a drop-in for the official configuration module if you want an actively versioned dependency, because the last tagged release is from 2019 and the package version in the manifest is 1.4.11. Verify first that the NestJS major version you run is within the peer dependency range the package declares, that the glob pattern you use excludes TypeScript definition files so production builds do not load them, and that the root path you set resolves to the right directory in both src and dist layouts.

## FAQ

### How do I register nestjs-config in a NestJS application?

Import ConfigModule from nestjs-config and add ConfigModule.load with a glob to the imports array of your module, for example ConfigModule.load(path.resolve(__dirname, 'config', '**/!(*.d).{ts,js}')). The glob loads every matching config file as a module whose default export is the config object.

### How do I control where nestjs-config loads config files from?

Either set ConfigService.rootPath before calling ConfigModule.load, which changes the default root directory, or call ConfigModule.resolveRootPath(__dirname) from any module before loading and pass a relative glob. In both cases the glob is relative to the src folder, or eventually the dist folder after compilation.

### What does the modifyConfigName option do in nestjs-config?

It rewrites the name a config is registered under, so a file named dogs.config.ts can be read as dogs.bark rather than dogs.config.bark. The example passes a function that strips the .config suffix, and the glob must be changed to match only config files such as **/!(*.d).config.{ts,js}.

### Why should the nestjs-config glob exclude TypeScript definition files?

The recommended pattern is config/**/!(*.d).{ts,js}, because in production, running in JavaScript after TypeScript compilation, the definition files should be disincluded. The documentation notes that config/**/*.ts is fine in dev environments but recommends the exclusion to avoid issues later when running in production.

## Sources

- [Issues](https://github.com/nestjsx/nestjs-config/issues)
- [License: MIT](https://github.com/nestjsx/nestjs-config/blob/master/LICENSE)
- [nestjsx/nestjs-config on GitHub](https://github.com/nestjsx/nestjs-config)
- [README](https://github.com/nestjsx/nestjs-config/blob/master/README.md)
- [Releases](https://github.com/nestjsx/nestjs-config/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nestjsx-nestjs-config
