@adaptably/adapt
v8.2.1
Published
Keep your Node.js application's configuration in one place. Access it simply and declaratively. Easily change it based on your application's environment.
Downloads
27
Readme
@adaptably/adapt
Keep your Node.js application's configuration in one place. Access it simply and declaratively. Easily change it based on your application's environment.
Installation
npm install @adaptably/adapt
Basic Usage
Setting Up
To get started, all you need is a Configuration File (adapt.json
) in the root of your project.
You can then use the Selecting Function to access your configuration data.
For example:
adapt.json
{
"app": {
"name": "hello-world"
}
}
app.js
import adapt from '@adaptably/adapt'
const appName = adapt('app.name')
console.log(appName)
CLI
node app.js
> "hello-world"
Adding Adaptive Objects
Maybe your app's configuration values change based on its environment. To handle this, add Adaptive Objects and the mode
variable.
adapt.json
{
"app": {
"names": {
"dev": "hello-world-dev",
"production": "hello-world-production"
}
}
}
app.js
import adapt from '@adaptably/adapt'
const appName = adapt('app.name')
console.log(appName)
CLI
mode=dev node app.js
> "hello-world-dev"
mode=production node app.js
> "hello-world-production"
Adding Adaptive Values
If there's configuration data you'd rather keep secret, bake in some Adaptive Values.
adapt
will look in the Environment File for these values. If it can't find them there, it will look in process.env
.
This is useful in production environments where the adapt.env.json
file may not exist.
Note: Values in the Environment File will take precedence over (but not replace) values in process.env
.
adapt.json
{
"api": {
"token": "[API_TOKEN]"
}
}
adapt.env.json
{
"API_TOKEN": "super-secret-token"
}
app.js
import adapt from '@adaptably/adapt'
const apiToken = adapt('api.token')
console.log(apiToken)
CLI
node app.js
> "super-secret-token"
Adaptive Values + Adaptive Objects
You can use Adaptive Objects in the Environment File too.
adapt.json
{
"app": {
"names": {
"dev": "hello-world-dev",
"production": "hello-world-production"
}
},
"api": {
"token": "[API_TOKEN]"
}
}
adapt.env.json
{
"API_TOKEN": {
"dev": "super-secret-token-dev",
"production": "super-secret-token-production"
}
}
app.js
import adapt from '@adaptably/adapt'
const apiToken = adapt('api.token')
console.log(apiToken)
CLI
mode=dev node app.js
> "super-secret-token-dev"
mode=production node app.js
> "super-secret-token-production"
Adding Adaptive References
Sometimes you may want to put configuration values together. In this case, you can use Adaptive References.
adapt.json
{
"database": {
"host": "127.0.0.1",
"user": "root",
"connectionString": "mysql://{database.user}@{database.host}"
}
}
app.js
import adapt from '@adaptably/adapt'
const connectionString = adapt('database.connectionString')
console.log(connectionString)
CLI
node app.js
> "mysql://[email protected]"
Putting It All Together
With our powers combined, make your application adaptable!
adapt.json
{
"allTheThings": "app-[APP_NAME]-with-{services.logging.key}",
"app": {
"name": "hello-world-[APP_NAME]"
},
"api": {
"tokens": {
"dev": "not-secret-token",
"staging": "[API_TOKEN]",
"production": "[API_TOKEN]"
}
},
"services": {
"logging": {
"keys": {
"_default": "default-logging-key",
"staging": "[LOGGING_KEY]",
"production": "[LOGGING_KEY]"
}
}
}
}
adapt.env.json
{
"APP_NAME": {
"dev": "from-environment-dev",
"production": "from-environment-production"
},
"API_TOKEN": "super-secret-token",
"LOGGING_KEY": {
"staging": "super-secret-logging-key-staging",
"production": "super-secret-logging-key-production"
}
}
app.js
import adapt from '@adaptably/adapt'
const allTheThings = adapt('allTheThings')
const appName = adapt('app.name')
const apiToken = adapt('api.token')
const loggingKey = adapt('services.logging.key')
console.log('App Name:', appName)
console.log('API Token:', apiToken)
console.log('Logging Key:', loggingKey)
console.log('All the Things:', allTheThings)
CLI
mode=dev node app.js
> "App Name: hello-world-from-environment-dev"
> "API Token: not-secret-token"
> "Logging Key: default-logging-key"
> "All the Things: app-from-environment-dev-with-default-logging-key"
mode=staging node app.js
> "App Name: hello-world-from-environment-staging"
> "API Token: super-secret-token"
> "Logging Key: super-secret-logging-key-staging"
> "All the Things: app-from-environment-staging-with-super-secret-logging-key-staging"
mode=production node app.js
> "App Name: hello-world-from-environment-production"
> "API Token: super-secret-token"
> "Logging Key: super-secret-logging-key-production"
> "All the Things: app-from-environment-staging-with-super-secret-logging-key-production"
Configuration
The Configuration File
The Configuration File lives at adapt.json
in the root of your project. To change the default location, see Customizing File Locations.
This file can contain Adaptive Objects and Adaptive Values which will be parsed by the Selecting Function.
Example Configuration File
{
"api": {
"keys": {
"_default": "bcd-234",
"staging": "abc-123"
"production": "[API_KEY]"
}
},
"general": {
"number": 5,
"text": "Hello world"
}
}
The Environment File
The Environment File lives at adapt.env.json
in the root of your project. To change the default location, see Customizing File Locations.
This file is optional and contains environment variables for your application. It typically should not be committed to source control.
The Environment File file can contain Adaptive Objects which will be parsed by the selecting function.
Example Environment Files
Basic
{
"API_KEY": "api-key",
"DATABASE_URL": "db-url"
}
With Adaptive Objects
{
"APP_ID": "abc123",
"API_KEY": {
"_default": "base-key",
"production": "production-key"
},
"DATABASE_URL": {
"dev": "dev-db",
"staging": "staging-db",
"production": "production-db"
}
}
Notes on the Environment File
- If the selecting function can't find a value in the Environment File, it will look in
process.env
. This is useful in production environments where theadapt.env.json
file may not exist. - Values in the Environment File will take precedence over (but not replace) values in
process.env
. - Values in the Environment File must be strings (in order to work with Adaptive Values in the Configuration File).
- Neither Adaptive Values nor Adaptive References can be used in the Environment File.
Customizing File Locations
When importing adapt
, it will look in thee root of your project for the Configuration and Environment files.
To customize where adapt
looks for these files, use the loadAdapt
function instead of the default export:
import { loadAdapt } from '@adaptably/adapt'
const adapt = loadAdapt({
configurationDirectory: '/my/custom/directory'
})
Programmatic Configuration
If you want to get even closer to the metal, you can skip file loading altogether and give adapt
the data it needs programatically using the createSelectingFunction
function:
import { createSelectingFunction } from '@adaptably/adapt'
const adapt = createSelectingFunction({
configuration: {
// Provide configuration data like in the Configuration File.
},
environment: {
// Provide environment data like in the Environment File.
},
// Optionally define a mode (or use `process.env.mode`).
mode: 'dev'
})
Detailed Usage
The Selecting Function
Usage
import adapt from '@adaptably/adapt'
Syntax
adapt(selector)
Arguments
| Name | Type | Description | Example |
| :-- | :-- | :-- | :-- |
| selector
| String | A string in dot notation which targets a key in the configuration file. | api.key
|
Exceptions
Throws a standard Error
if:
- The selector is
null
orundefined
. - A matching basic or Adaptive Object key cannot be found in the Configuration File.
- The value of a selected key is
undefined
.
Normal Keys
Normal keys can be accessed by name, using dot notation.
Using the Example Configuration File:
adapt('api.keys.staging') // 'abc-123'
adapt('general.number') // 5
adapt('general.text') // 'Hello world'
Adaptive Objects
In the Configuration File
In the Configuration File, Adaptive Objects have two special qualities:
- The key which contains them is plural (i.e.
tokens
), - They are an object with keys representing application modes, such as:
"keys": {
"dev": "key-dev",
"staging": "key-staging"
}
Adaptive Objects in the Configuration File will be accessed when the singular form of a given selector (api.key
) cannot be found.
A key from the object will be chosen based on the mode
variable.
See Adding Adaptive Objects for an example.
In the Environment File
In the Environment File, Adaptive Objects have one special quality:
- They are an object with keys representing application modes, such as:
"API_TOKEN": {
"dev": "token-dev",
"staging": "token-staging"
}
Adaptive Objects in the Environment File will be accessed when the Configuration File contains an Adaptive Value.
A key from the object will be chosen based on the mode
variable.
See Adaptive Values + Adaptive Objects for an example.
The Default Key
All Adaptive Objects can contain a _default
key.
This key will be accessed if the mode
variable is not set, or if there are no keys within the Adaptive Object which match the current mode
.
See Putting it All Together for an example.
The mode
Variable
Adaptive Objects can only function if the mode
is set.
By default, adapt
will look in the environment for process.env.mode
.
The mode
variable can also be set manually using Programmatic Configuration.
Adaptive Values
Adaptive Values are used in the Configuration File to reference data in the environment.
These values are surrounded in brackets, such as [API_KEY]
. This tells the Selecting Function to replace this section with a value from the Environment File or process.env
.
See Adding Adaptive Values for an example.
Interpolated Adaptive Values
Adaptive Values can contain additional text and/or multiple bracketed sections. For example:
{
"database": "mysql://[DATABASE_USER]:[DATABASE_PASSWORD]"
}
Adaptive References
Adaptive References are used in the Configuration File to reference other configuration values.
These values are surrounded in curly braces which contain a selector, such as {database.url}
. This tells the Selecting Function to replace this section with the value of the given selector from elsewhere in the Configuration File.
See Adding Adaptive Values for an example.
Interpolated Adaptive References
Adaptive References can contain additional text and/or multiple curly braced sections. For example:
{
"database": "mysql://{database.user}@{database.host}"
}
See Putting it All Together for an example.
Preloading Environment Data
By default, adapt
will not load data from the Environment File into process.env
. If you'd like to do this you can use the adapt
command.
The adapt
Command
The adapt
command will run shell commands with the environment variables defined in your Environment File loaded into process.env
.
This can be useful when running a command line utility which relies on environment variables.
Usage
adapt '[command]'
Example
adapt.env.json
{
"DATABASE_URL": {
"testing": "my-testing-database",
"production": "my-production-database"
}
}
CLI
mode=testing adapt "echo $DATABASE_URL"
> "my-testing-database"
mode=production adapt "echo $DATABASE_URL"
> "my-production-database"