8.2.1 • Published 1 year ago

@adaptably/adapt v8.2.1

Weekly downloads
-
License
-
Repository
github
Last release
1 year ago

@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://root@127.0.0.1"

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 the adapt.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

NameTypeDescriptionExample
selectorStringA string in dot notation which targets a key in the configuration file.api.key

Exceptions

Throws a standard Error if:

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"
8.2.1

1 year ago

8.2.0

1 year ago

8.1.0

1 year ago

8.0.6

1 year ago

8.0.5

1 year ago

8.0.4

1 year ago

8.0.3

1 year ago

8.0.2

1 year ago

8.0.1

1 year ago

8.0.0

1 year ago

7.0.0

1 year ago

1.0.0

1 year ago