Skip to main content

Sources

A sequence of composable ConfigurationSource implementations provide field assignments to be processed by the schema.

Each ConfigurationSource implements a load method that returns a map of field paths (hierarchical dotted child schema names and field names) associated with value assignments.

For example, the field assignments output from a source might look like:

{
"debug": true,
"basics.verbose": true,
"basics.codes": [ "5xx", "z10", "123" ],
"server.host": "localhost",
"server.port": "8081",
"server.protocol": "https"
}

technically, it would be the Map equivalent.

Sources each have a defined priority sequence number. The highest numbered configuration source in the sequence defining a field path assignment takes precedence. These are defined in ConfigurationSource.DefaultSequence:

  SYSTEM_DEFAULTS: 100    (priority used by schema defaults)
MODULES: 200 (used by ModuleManager for dependency injection)
SECRETS: 300 (for extension libraries to use)
APP_DEFAULTS: 400 (applications may reuse schemas and have their own defaults)
ENVIRONMENT: 500 (environment variables)
ARGUMENTS: 600 (command line)
SERVER: 700 (externally loaded configuration)
CONFIGURATION: 800 (config files)
OVERRIDES: 900 (forced settings from the application)

(Using an explicit sequence number allows the registration of a user-provided source to be easily sorted into the set of existing sources.)

note

Unlike some configuration libraries, Configurator defaults to prioritizing config files over the command line and environment variables. This is to enable hot-reloading of configuration. The environment and command line are immutable once the application starts, but a config file can be updated dynamically!

It isn't magic.

If you don't pass in your own source list to Configurator, it simply creates a default list that aligns with ConfigurationSource.DefaultSequence.

  this.registerConfigurationSource(new DefaultsSource());     // system/schema defaults
this.registerConfigurationSource(new ObjectSource({
contextFieldName: 'defaults'
})); // app defaults
this.registerConfigurationSource(new EnvironmentSource());
this.registerConfigurationSource(new CommandLineSource());
this.registerConfigurationSource(new JsonFileSource({
contextFieldName: configContextFieldName
}));
this.registerConfigurationSource(new ObjectSource({
contextFieldName: 'overrides', sequence: ConfigurationSource.DefaultSequence.OVERRIDES
}));

ConfigurationSource

This section is only interesting if you are implementing your own source.

ConfigurationSource is an abstract base class that must be initialized via super:

constructor(name, options)

constructor(options)

ParamTypeDescription
namestringname of the source
[options]objectOptional settings (described below)
[options.name]stringAlternative way to specify the name
[options.sequence]numberPriority for this source (default: 1000)

You must provide an implementation for load that returns a Map from a dotted field path to the configured value (that will later be resolved by a type resolver).

It is acceptable to peek at the field type in order to interpret inputs; (most commonly to identify array-like fields).

async load(schema:ConfigurationSchema, context:object, options:object):Map<string,any>

ParamTypeDescription
schemaConfigurationSchemaschema to use for field paths
contextobjectthe context object passed to Configurator.configure
[options]stringload options (only one at the moment)
[options.strict]booleanwhether to throw an error for unknown settings

Returns: Map<string,any>

Example:

class MyConfigurationSource extends ConfigurationSource {
constructor(name, options) {
super(name, options);
}

async load(schema, context, options) {
const fieldPaths = schema.getAllFieldPaths();
const fieldAssignments = new Map();

// source logic here

return fieldAssignments;
}
}