configuration_service 1.0.0 configuration_service: ^1.0.0 copied to clipboard
Enables applications to read JSON configuration files
Enables applications to make effective use of JSON configuration files. Especially useful for backend services or CLI tools written in Dart but can also be used with Flutter.
Features #
- Type-safe processing of JSON configuration files
- No need for code generation
- Handles optional and required values
- Can use defaults if values are missing
- Supports environment variables (great to inject secrets)
Getting started #
Assume a configuration file config.json
with the following contents:
{
"enable_graphql_playground": true,
"enable_introspection": true,
"enable_schema_download": true
}
To load such a configuration file in your application, create a class
MyConfigurationService
that extends ConfigurationService
and defines
typed properties for every configuration value you want to support:
class MyConfigurationService extends ConfigurationService {
// ConfigurationValue<bool> defines a boolean value. You can use bool,
// int, String, List<> as well as your own complex types (more below).
final enableGraphQLPlayground = ConfigurationValue<bool>(
// name of the value in the configuration file
name: "enable_graphql_playground",
// optionally specify a default that will be used the value doesn't exist in the configuration file
defaultValue: const Optional(false),
);
final enableIntrospection = ConfigurationValue<bool>(
name: "enable_introspection",
// Values can be marked as required. This will throw an exception if the value is missing in the configuration file.
isRequired: true,
);
final enableSchemaDownload = ConfigurationValue<bool>(
name: "enable_schema_download",
defaultValue: const Optional(false),
);
MyConfigurationService() {
// register all configuration values so that they are picked up by the loader
register(enableGraphQLPlayground);
register(enableIntrospection);
register(enableSchemaDownload);
}
}
Load the configuration file with loadFromJson()
:
void main() async {
final configurationService = MyConfigurationService();
await configurationService.loadFromJson(filePath: 'config.json');
print(configurationService.enableIntrospection.value);
}
Usage #
Access a value from the configuration file #
Assume the following configuration file exists:
{ "test": true }
The following code will read it:
final configurationValue = ConfigurationValue<bool>(name: 'test');
final configurationService = ConfigurationService();
configurationService.register(configurationValue);
await configurationService.loadFromJson(filePath: 'config.json');
print(configurationValue.value); // "true"
Check if a value exists #
final configurationValue = ConfigurationValue<bool>(name: 'test');
final configurationService = ConfigurationService();
configurationService.register(configurationValue);
await configurationService.loadFromJson(filePath: 'config.json');
print(configurationValue.hasValue()); // "true" if value exists in config.json
Use defaults #
final configurationValue = ConfigurationValue<bool>(name: 'test', defaultValue = Optional(true));
final configurationService = ConfigurationService();
configurationService.register(configurationValue);
await configurationService.loadFromJson(filePath: 'config.json');
print(configurationValue.hasValue()); // "true" even if it doesn't exist in config.json
print(configurationValue.hasValue(ignoreDefaults: true)); // only "true" if it exists in config.json
Mark values as required #
final configurationValue = ConfigurationValue<bool>(name: 'test', isRequired: true);
final configurationService = ConfigurationService();
configurationService.register(configurationValue);
// will throw an exception if "test" can't be found in the file
await configurationService.loadFromJson(filePath: 'config.json');
print(configurationValue.value);
Allow overrides from environment variables #
Using allowEnvironmentOverrides
gives precedence to values found in the environment.
Names of environment variables are determined by re-casing the configuration value's name
to CONSTANT_CASE
.
Examples:
verify_token
becomesVERIFY_TOKEN
auth.verify_token
becomesAUTH_VERIFY_TOKEN
final configurationValue = ConfigurationValue<bool>(name: 'test', isRequired: true);
final configurationService = ConfigurationService();
configurationService.register(configurationValue);
await configurationService.loadFromJson(filePath: 'config.json', allowEnvironmentOverrides: true);
print(configurationValue.value); // value that was found in env variable TEST, otherwise value from config file
Using configuration file sections #
It is often desirable to partition a configuration file in section for better maintainability. A hierarchy of configuration value can be specified by using the dot notation.
Assume the following configuration file:
{
"auth": {
"verify_token": true
}
}
The value can be accessed by specifying auth.verify_token
as its name.
Using complex types #
configuration_service
understands int
, String
and bool
natively as well as
corresponding List
types. To load custom complex types, specify a deserializer
when registering the type:
configurationService.register(authEntityIdFallback, deserializers: {
EntityIdFallbackConfig: EntityIdFallbackConfig.fromJson,
});
This allows loading arbitrarily structured data from the configuration file. Make sure
that the fromJson
method uses correct json keys to access the values from the
configuration data.