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 customConfigValidator. When provided, the loaded object is validated and coerced before binding.serviceIdentifier(optional): Service identifier used to bind theConfigService. Defaults toconfigServiceIdentifier.
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();
}
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.