Skip to main content

API Reference

This section covers the core API of @inversifyjs/config.

ConfigContainerModule

ConfigContainerModule loads configuration from a single ConfigSource, optionally validates it, and binds a ConfigService.

class ConfigContainerModule extends ContainerModule {
static fromOptions<TConfig>(
options: ConfigContainerModuleOptions<TConfig>,
): ConfigContainerModule;
}

ConfigContainerModuleOptions

interface ConfigContainerModuleOptions<TConfig = ConfigObject> {
source: ConfigSource;
validate?: ConfigValidator<TConfig> | StandardSchemaV1<TConfig>;
serviceIdentifier?: ServiceIdentifier<ConfigService<TConfig>>;
}

Parameters:

  • source (ConfigSource): Loads a plain configuration object.
  • validate (optional): A Standard Schema or a custom ConfigValidator. When provided, the loaded object is validated and coerced before binding.
  • serviceIdentifier (optional): Service identifier used to bind the ConfigService. Defaults to configServiceIdentifier.

Example: Loading and validating config

import {
ConfigContainerModule,
type ConfigService,
configServiceIdentifier,
object,
} from '@inversifyjs/config';
import { Container } from 'inversify';
import { z } from 'zod';

const appConfigSchema = z.object({
DATABASE_URL: z.url(),
PORT: z.coerce.number().default(3000),
});

type AppConfig = z.infer<typeof appConfigSchema>;

export async function bootstrap(): Promise<AppConfig> {
const container: Container = new Container();

await container.loadAsync(
ConfigContainerModule.fromOptions({
source: object({
DATABASE_URL: 'postgres://localhost:5432/app',
PORT: '3000',
}),
validate: appConfigSchema,
}),
);

return container.get<ConfigService<AppConfig>>(configServiceIdentifier).get();
}
warning

Always use container.loadAsync(...). The module load callback is asynchronous because sources and validators may perform I/O.

ConfigService

ConfigService exposes the loaded (and optionally validated) configuration object.

interface ConfigService<TConfig> {
get(): TConfig;
}

By default it is bound under configServiceIdentifier:

const configServiceIdentifier: unique symbol = Symbol.for(
'@inversifyjs/config/configService',
);

Example: Injecting ConfigService

import {
type ConfigService,
configServiceIdentifier,
} from '@inversifyjs/config';
import { inject, injectable } from 'inversify';

interface AppConfig {
PORT: number;
}

@injectable()
export class App {
readonly #configService: ConfigService<AppConfig>;

constructor(
@inject(configServiceIdentifier)
configService: ConfigService<AppConfig>,
) {
this.#configService = configService;
}

public getPort(): number {
return this.#configService.get().PORT;
}
}

Example: Custom service identifier

Use a custom service identifier when you need multiple configs in the same container:

import {
ConfigContainerModule,
type ConfigService,
object,
} from '@inversifyjs/config';
import { Container, type ServiceIdentifier } from 'inversify';

export const appConfigServiceIdentifier: ServiceIdentifier<
ConfigService<{ HOST: string }>
> = Symbol.for('appConfigService');

export async function bootstrap(): Promise<{ HOST: string }> {
const container: Container = new Container();

await container.loadAsync(
ConfigContainerModule.fromOptions({
serviceIdentifier: appConfigServiceIdentifier,
source: object({ HOST: 'localhost' }),
}),
);

return container.get(appConfigServiceIdentifier).get();
}

Validation

Validation is optional. When provided, it runs after the source loads and before the ConfigService is bound.

Standard Schema

Any Standard Schema implementation works (Zod, Valibot, ArkType, and others):

import {
ConfigContainerModule,
type ConfigService,
configServiceIdentifier,
object,
} from '@inversifyjs/config';
import { Container } from 'inversify';
import { z } from 'zod';

const appConfigSchema = z.object({
NODE_ENV: z
.enum(['development', 'production', 'test'])
.default('development'),
PORT: z.coerce.number().min(1).max(65535).default(3000),
});

type AppConfig = z.infer<typeof appConfigSchema>;

export async function bootstrap(): Promise<AppConfig> {
const container: Container = new Container();

await container.loadAsync(
ConfigContainerModule.fromOptions({
source: object({
NODE_ENV: 'production',
PORT: '8080',
}),
validate: appConfigSchema,
}),
);

return container.get<ConfigService<AppConfig>>(configServiceIdentifier).get();
}

ConfigValidator

For custom logic, pass an object with a validate method:

interface ConfigValidator<TConfig> {
validate(input: ConfigObject): TConfig | Promise<TConfig>;
}
import {
ConfigContainerModule,
type ConfigObject,
type ConfigService,
configServiceIdentifier,
type ConfigValidator,
object,
} from '@inversifyjs/config';
import { Container } from 'inversify';

interface AppConfig {
port: number;
}

const appConfigValidator: ConfigValidator<AppConfig> = {
validate(input: ConfigObject): AppConfig {
const port: number = Number(input['port']);

if (!Number.isInteger(port) || port < 1) {
throw new Error('port must be a positive integer');
}

return { port };
},
};

export async function bootstrap(): Promise<AppConfig> {
const container: Container = new Container();

await container.loadAsync(
ConfigContainerModule.fromOptions({
source: object({ port: '3000' }),
validate: appConfigValidator,
}),
);

return container.get<ConfigService<AppConfig>>(configServiceIdentifier).get();
}

If validation fails, an InversifyConfigError (or an error thrown by your validator) prevents the module from loading.

ConfigSource

A config source produces a plain object:

interface ConfigSource {
load(): ConfigObject | Promise<ConfigObject>;
}

See Sources for built-in helpers and optional packages.