Configuration
Load, validate, and inject application configuration through the DI container. Domain services stay on @di-framework/core; this package is the typed config layer.
Features
Sources:
envSource,objectSource,jsonFileSource,yamlFileSource,tomlFileSource(deep-merged left → right).Profiles:
@WithProfileoverlays{profile}.config.{ext}next to the base file.Validation: pluggable
ConfigSchema— optional Zod adapter at@di-framework/config/zod.DI registration:
registerConfigexposes the root object plus flattened dotted paths.Decorators:
@Configuration/@Value/@WithProfilematch the rest of the framework.Imperative API:
loadConfig/loadAndRegisterConfigfor scripts and tests.
Installation
Decorators need TypeScript 5 and experimentalDecorators. emitDecoratorMetadata is not required.
Quick Start
With APP_PORT=8080 and APP_DATABASE__HOST=db.internal, db.port is 8080 and db.host is db.internal.
@Configuration builds defaults from class property initializers, merges sources, registers the result under the 'config' token (with flattened paths), and registers the class as a singleton holding the loaded snapshot.
Imperative API
Prefer this when sources are async or you want explicit control in bootstrap code:
Sync variants: loadConfigSync/loadAndRegisterConfigSync (all sources must return plain objects, not Promises).
Zod Validation
Any object with a parse(input) method can implement ConfigSchema. Use schemaFromParse to wrap a plain function.
Env Mapping
Option | Default | Meaning |
|---|---|---|
|
| Only keys with this prefix; prefix is stripped |
|
| Nesting delimiter after strip |
|
| Segment transform ( |
|
| Parse booleans, numbers, JSON literals |
APP_DB__HOST=localhost → { db: { host: 'localhost' } }.
Injecting Values
With flatten: true (the default), every dotted path is a DI token. @Value('database.host') is equivalent to @Component('config.database.host').
API Reference
Export | Description |
|---|---|
| Merge defaults + sources (+ schema + profiles) |
| Put config (and paths) on the container |
| Load then register |
| Built-in sources |
| Decorators |
| Process-wide selected profiles |
| Resolve |
| Schema helpers |
|
|
|
|
|
|
File sources
JSON parsing is built in. YAML needs the optional peer yaml; TOML needs smol-toml. Importing @di-framework/config does not load those parsers until yamlFileSource/tomlFileSource actually load(). Dedicated subpaths @di-framework/config/yaml and @di-framework/config/toml export the same functions.
All three file sources:
Require a plain-object root (arrays, primitives, and
nullthrow).Use
optional: trueso a missing base file (ENOENT) yields{}. Invalid syntax still throws.Label errors as
json:,yaml:, ortoml:plus the path.Do not support multi-document YAML streams.
Profiles
When a profile is selected, each file source loads the base file, then deep-merges {profile}.config.{ext} from the same directory. The overlay name is always {profile}.config.{ext} — it does not depend on the base file stem.
Base file | Selected profile | Overlay |
|---|---|---|
|
|
|
|
|
|
|
|
|
Select profiles with, in order of precedence for a given source:
yamlFileSource(path, { profiles: ['dev'] })(and the JSON/TOML equivalents)@WithProfile('dev')on the@Configurationclass, orloadConfig({ profiles: ['dev'] })setSelectedProfiles('dev')
Several profiles merge left → right (@WithProfile('dev', 'local') applies dev.config.yaml then local.config.yaml). Missing overlay files are skipped; invalid names (.., path separators, empty) throw. optional on the source applies only to the base file.
Non-goals (v1)
Remote config providers, live reload / watch, and secret managers. Implement ConfigSource/ConfigSchema for those.
Example
A worked example lives in the config example package.