# How to Create Dynamic Modules in NestJS: The Complete Guide

> Learn to create dynamic modules in NestJS using register and registerAsync factory methods. Build reusable, configurable modules with runtime metadata for your applications.

- Repository: [nestjs/nest](https://github.com/nestjs/nest)
- Tags: how-to-guide
- Published: 2026-03-01

---

**Dynamic modules in NestJS are reusable, configurable modules created by static factory methods like `register()` or `registerAsync()` that return a `DynamicModule` object containing the module class plus runtime metadata such as providers, imports, and exports.**

A dynamic module allows you to pass configuration into a module at the moment you import it, making the module system in the `nestjs/nest` repository flexible enough to handle database connections, configuration services, and third-party integrations. Unlike static modules, which have fixed metadata, dynamic modules conform to the `DynamicModule` interface exported from `@nestjs/common` and are processed by Nest's internal scanner at bootstrap time.

## What Are Dynamic Modules in NestJS?

A **dynamic module** is a module definition object that contains a `module` property pointing to the module class, alongside additional metadata like `providers`, `imports`, and `exports`. This object is returned by a static method—conventionally named `register()`, `forRoot()`, or `forFeature()`—that accepts configuration arguments.

According to the NestJS source code, the `DynamicModule` interface is a simple contract: it requires a `module` property (the class reference) and allows all other standard module metadata. When you call `ConfigModule.register({ folder: './config' })`, you are not importing the class directly; you are invoking a factory that returns this structured object.

## How NestJS Detects and Processes Dynamic Modules

The framework handles dynamic modules through a specific internal pipeline involving the scanner, compiler, and container.

### Detection in the Scanner

When Nest boots, the `Scanner` class iterates over all module definitions to determine which are dynamic. In [`packages/core/scanner.ts`](https://github.com/nestjs/nest/blob/main/packages/core/scanner.ts) at lines 716-719, the scanner uses the `isDynamicModule` utility to check for the presence of a `module` property:

```typescript
// Simplified logic from packages/core/scanner.ts#L716-L719
if (isDynamicModule(moduleDefinition)) {
  const { module: moduleType, ...metadata } = moduleDefinition;
  // Process as dynamic module
}

```

If the object contains a `module` key, Nest extracts the class and treats the remaining properties as metadata overrides.

### Compilation and Metadata Merging

After detection, the `Compiler` class merges the dynamic metadata into the module tree. In [`packages/core/injector/compiler.ts`](https://github.com/nestjs/nest/blob/main/packages/core/injector/compiler.ts) at lines 65-68, the compiler separates the dynamic module's extra providers and imports from the base module class, combining them into the final dependency graph:

```typescript
// Conceptual view of packages/core/injector/compiler.ts#L65-L68
const dynamicMetadata = omit(dynamicModule, 'module');
// Merge into existing module metadata

```

### Container Registration

The `Container` class stores dynamic module metadata for later resolution. In [`packages/core/injector/container.ts`](https://github.com/nestjs/nest/blob/main/packages/core/injector/container.ts) at lines 201-210, the `addDynamicModules` method walks the imports array, recursively registers any nested dynamic modules, and caches the partial metadata:

```typescript
// From packages/core/injector/container.ts#L201-L210
async addDynamicModules(modules: any[], scope: Type<any>[]) {
  for (const moduleDef of modules) {
    const isDyn = this.isDynamicModule(moduleDef);
    const token = isDyn ? moduleDef.module : moduleDef;
    this.dynamicModuleMetadata.set(token.name, moduleDef);
    if (isDyn && moduleDef.imports) {
      await this.addDynamicModules(moduleDef.imports, scope);
    }
  }
}

```

## Creating a Basic Dynamic Module

To implement a synchronous dynamic module, define a static `register()` method that returns a `DynamicModule` object. The following example from [`sample/25-dynamic-modules/src/config/config.module.ts`](https://github.com/nestjs/nest/blob/main/sample/25-dynamic-modules/src/config/config.module.ts) demonstrates a configuration module that accepts a folder path:

```typescript
// src/config/config.module.ts
import { DynamicModule, Module } from '@nestjs/common';
import { ConfigService } from './config.service';
import { CONFIG_OPTIONS } from './constants';

export interface ConfigModuleOptions {
  folder: string;
}

@Module({})
export class ConfigModule {
  static register(options: ConfigModuleOptions): DynamicModule {
    return {
      module: ConfigModule,
      providers: [
        {
          provide: CONFIG_OPTIONS,
          useValue: options,
        },
        ConfigService,
      ],
      exports: [ConfigService],
    };
  }
}

```

In this implementation:
- The `module` property identifies the class that owns the metadata.
- A custom provider injects the `options` object using the `CONFIG_OPTIONS` injection token.
- `ConfigService` can now access these options via constructor injection.

### Consuming the Dynamic Module

Import the module using the static factory method instead of the class reference:

```typescript
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from './config/config.module';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    ConfigModule.register({ folder: './config' }),
    UsersModule,
  ],
})
export class AppModule {}

```

## Async Dynamic Module Registration

For asynchronous configuration—such as loading secrets from a remote vault—use the `registerAsync()` pattern with `useFactory` providers. This approach allows you to resolve dependencies asynchronously before the module is fully instantiated:

```typescript
// src/database/database.module.ts
import { DynamicModule, Module } from '@nestjs/common';
import { createConnection } from 'typeorm';
import { DATABASE_OPTIONS } from './constants';

@Module({})
export class DatabaseModule {
  static registerAsync(): DynamicModule {
    return {
      module: DatabaseModule,
      imports: [],
      providers: [
        {
          provide: DATABASE_OPTIONS,
          useFactory: async () => {
            const secret = await fetchSecret('db-credentials');
            return {
              type: 'postgres',
              host: secret.host,
              username: secret.user,
              password: secret.pass,
              database: secret.db,
            };
          },
        },
        {
          provide: 'DB_CONNECTION',
          useFactory: async (opts: any) => await createConnection(opts),
          inject: [DATABASE_OPTIONS],
        },
      ],
      exports: ['DB_CONNECTION'],
    };
  }
}

```

The `registerAsync` method returns the same `DynamicModule` structure, but the providers use factory functions that return Promises. Nest waits for these promises to resolve before finishing the module initialization.

## Advanced: Accessing Dynamic Modules at Runtime

Once registered, dynamic modules are accessible via the `ModuleRef` utility. In [`packages/core/nest-application-context.ts`](https://github.com/nestjs/nest/blob/main/packages/core/nest-application-context.ts) at lines 93-107, the `select` method resolves the module token to the stored dynamic metadata, allowing you to retrieve providers with the concrete values supplied at registration:

```typescript
// From packages/core/nest-application-context.ts#L93-L107
select<T>(moduleType: Type<T>): T {
  const moduleRef = this.container.getModule(moduleType);
  // Returns module reference with dynamic metadata applied
}

```

This enables runtime introspection of dynamically configured providers within the selected module scope.

## Summary

- **Dynamic modules** in NestJS are created by static methods returning a `DynamicModule` object with a `module` property and runtime metadata.
- The **Scanner** detects dynamic modules via `isDynamicModule` in [`packages/core/scanner.ts`](https://github.com/nestjs/nest/blob/main/packages/core/scanner.ts), while the **Compiler** merges metadata in [`packages/core/injector/compiler.ts`](https://github.com/nestjs/nest/blob/main/packages/core/injector/compiler.ts).
- The **Container** registers dynamic modules recursively through `addDynamicModules` in [`packages/core/injector/container.ts`](https://github.com/nestjs/nest/blob/main/packages/core/injector/container.ts).
- Use **`register()`** for synchronous configuration and **`registerAsync()`** for asynchronous setup involving factory providers.
- Dynamic modules enable reusable, configurable integrations that accept parameters at import time rather than at definition time.

## Frequently Asked Questions

### What is the difference between `register()` and `registerAsync()` in NestJS dynamic modules?

**`register()`** is a synchronous factory method that accepts static configuration options and immediately returns a `DynamicModule` object with `useValue` providers. **`registerAsync()`** supports asynchronous operations, allowing you to use `useFactory`, `useClass`, or `useExisting` providers that return Promises, making it ideal for loading configuration from environment variables, remote secrets managers, or databases at bootstrap time.

### How does NestJS distinguish between a static module and a dynamic module?

NestJS checks for the presence of a `module` property on the imported object. According to `packages/core/scanner.ts#L716-L719`, the internal `isDynamicModule` function returns true if the definition is an object containing a `module` key pointing to a class constructor. Static modules are class references passed directly, while dynamic modules are objects returned by static factory methods.

### Can dynamic modules in NestJS import other dynamic modules?

Yes, dynamic modules can import other dynamic modules. In `packages/core/injector/container.ts#L201-L210`, the `addDynamicModules` method recursively processes the `imports` array of a dynamic module, detecting and registering any nested dynamic definitions. This allows you to compose complex module trees where configuration flows through multiple levels of dynamic factories.

### What is the `DynamicModule` interface and where is it defined?

The **`DynamicModule`** interface is defined in `@nestjs/common` and specifies the contract for dynamic module objects. It requires a `module` property (the class type) and optionally accepts `imports`, `controllers`, `providers`, and `exports`. This interface allows Nest's dependency injection system to treat runtime-generated module metadata identically to static `@Module()` decorator metadata.