Dynamic configuration values with variable support.
Works with yml
, json
, toml
config formats and anything that parsed down to a plain ol' javascript object
Configorama extends your configuration with a powerful variable system. It resolves configuration variables from:
- CLI options
- ENV variables
- File references
- Other Key/values in config
- Async/sync JS functions
- Any source you'd like...
See tests for more examples.
Click to expand
Async API:
const path = require('path')
const configorama = require('configorama')
const cliFlags = require('minimist')(process.argv.slice(2))
// Path to yaml/json/toml config
const myConfigFilePath = path.join(__dirname, 'config.yml')
const config = await configorama(myConfigFilePath, {
options: args
})
Sync API:
const path = require('path')
const configorama = require('configorama')
const cliFlags = require('minimist')(process.argv.slice(2))
// Path to yaml/json/toml config
const myConfigFilePath = path.join(__dirname, 'config.yml')
const config = configorama.sync(myConfigFilePath, {
options: cliFlags
})
apiKey: ${env:SECRET_KEY}
# Fallback to default value if env var not found
apiKeyWithFallback: ${env:SECRET_KEY, 'defaultApiKey'}
# CLI option. Example `cmd --stage dev` makes `bar: dev`
bar: ${opt:stage}
# Composed example makes `foo: dev-hello`
foo: ${opt:stage}-hello
# You can also provide a default value. If no --stage flag is provided, it will use 'dev'
foo: ${opt:stage, 'dev'}
foo: bar
zaz:
matazaz: 1
wow:
cool: 2
# Self file reference. Resolves to `bar`
one: ${self:foo}
# Shorthand self reference. Resolves to `bar`
two: ${foo}
# Dot prop reference will traverse the object. Resolves to `2`
three: ${zaz.wow.cool}
# Import full yml/json/toml file via relative path
fileRef: ${file(./subFile.yml)}
# Import sub values from files. This imports other-config.yml `topLevel:` value
fileValue: ${file(./other-config.yml):topLevel}
# Import sub values from files. This imports other-config.json `nested.value` value
fileValueSubKey: ${file(./other-config.json):nested.value}
# Fallback to default value if file not found
fallbackValueExample: ${file(./not-found.yml), 'fall back value'}
asyncJSValue: ${file(./async-value.js)}
# resolves to 'asyncval'
${file(./asyncValue.js)}
will call into async-value
and run/resolve the async function with values. These values can be strings, objects, arrays, whatever.
/* async-value.js */
function delay(t, v) {
return new Promise((resolve) => setTimeout(resolve.bind(null, v), t))
}
async function fetchSecretsFromRemoteStore(config) {
await delay(1000)
return 'asyncval'
}
module.exports = fetchSecretsFromRemoteStore
Resolve values from cwd
git data.
########################
# Git Variables
########################
# Repo owner/name. E.g. DavidWells/configorama
repo: ${git:repo}
repository: ${git:repository}
# Repo owner. E.g. DavidWells
owner: ${git:owner}
repoOwner: ${git:repoOwner}
repoOwnerDashed: ${git:repo-owner}
# Url. E.g. https://github.com/DavidWells/configorama
url: ${git:url}
repoUrl: ${git:repoUrl}
repoUrlDashed: ${git:repo-url}
# Directory. E.g. https://github.com/DavidWells/configorama/tree/master/tests/gitVariables
dir: ${git:dir}
directory: ${git:directory}
# Branch
branch: ${git:branch}
# Commits. E.g. 785fa6b982d67b079d53099d57c27fa87c075211
commit: ${git:commit}
# Sha1. E.g. 785fa6b
sha1: ${git:sha1}
# Message. E.g. 'Initial commit'
message: ${git:message}
# Remotes. E.g. https://github.com/DavidWells/configorama
remote: ${git:remote}
remoteDefined: ${git:remote('origin')}
remoteDefinedNoQuotes: ${git:remote(origin)}
# Tags. E.g. v0.5.2-1-g785fa6b
tag: ${git:tag}
# Describe. E.g. v0.5.2-1-g785fa6b
describe: ${git:describe}
# Timestamp. E.g. 2025-01-28T07:28:53.000Z
gitTimestampRelativePath: ${git:timestamp('../../package.json')}
# Timestamp. E.g. 2025-01-28T07:28:53.000Z
gitTimestampAbsolutePath: ${git:timestamp('package.json')}
Convert human-readable time expressions into cron expressions. Supports single quotes for values.
# Basic patterns
everyMinute: ${cron('every minute')} # * * * * *
everyHour: ${cron('every hour')} # 0 * * * *
everyDay: ${cron('every day')} # 0 0 * * *
weekdays: ${cron('weekdays')} # 0 0 * * 1-5
midnight: ${cron('midnight')} # 0 0 * * *
noon: ${cron('noon')} # 0 12 * * *
# Interval patterns
every5Minutes: ${cron('every 5 minutes')} # */5 * * * *
every15Minutes: ${cron('every 15 minutes')} # */15 * * * *
every2Hours: ${cron('every 2 hours')} # 0 */2 * * *
every3Days: ${cron('every 3 days')} # 0 0 */3 * * *
# Specific times
at930: ${cron('at 9:30')} # 30 9 * * *
at930pm: ${cron('at 9:30 pm')} # 30 21 * * *
at1200: ${cron('at 12:00')} # 0 12 * * *
at1230am: ${cron('at 12:30 am')} # 30 0 * * *
# Weekday patterns
mondayMorning: ${cron('on monday at 9:00')} # 0 9 * * 1
fridayEvening: ${cron('on friday at 17:00')} # 0 17 * * 5
sundayNoon: ${cron('on sunday at 12:00')} # 0 12 * * 0
# Pre-existing cron expressions
customCron: ${cron('15 2 * * *')} # 15 2 * * *
Filters will transform the resolved variables
toUpperCaseString: ${'value' | toUpperCase }
toKebabCaseString: ${'valueHere' | toKebabCase }
key: lol_hi
keyTwo: lol_hi
toKebabCase: ${key | toKebabCase }
toCamelCase: ${keyTwo | toCamelCase }
Functions will convert resolved config values with various methods.
object:
one: once
two: twice
objectTwo:
three: third
four: fourth
mergeObjects: ${merge(${object}, ${objectTwo})}
See the tests folder for a bunch of examples!
Configorama allows you to bring your own variable sources.
There are 2 ways to resolve variables from custom sources.
-
Use the baked in javascript method for sync or aysnc resolution.
-
Add your own variable syntax and resolver.
const config = configorama('path/to/configFile', { variableSources: [{ // Match variables ${consul:xyz} match: RegExp(/^consul:/g), // Custom variable source. Must return a promise resolver: (varToProcess, opts, currentObject) => { // Make remote call to consul return Promise.resolve(varToProcess) } }] }) console.log(config)
This would match the following config:
key: ${consul:xyz}
Q: Why should I use this?
Never rendering a stale configuration file again!
Q: Does this work with serverless.yml
Yes it does. Using serverless.js
as your main entry point!
/* serverless.js */
const path = require('path')
const configorama = require('configorama')
const args = require('minimist')(process.argv.slice(2))
// Path to serverless config to be parsed
const yamlFile = path.join(__dirname, 'serverless.config.yml')
module.exports = configorama.sync(yamlFile, { options: args })
How is this different than the serverless variable system?
-
You can use it with any other tool you'd like. Just include
configorama
and go nuts. -
It's pluggable. Add whatever variable syntax/sources you wish.
-
Filters! You can filter values before they are resolved.
key: ${opt:stage | toUpperCase}
-
Cleaner self references
keyOne: subKey: hi # Before key: ${self:keyOne.subKey} # Now key: ${keyOne.subKey}
-
Numbers as defaults are supported
key: ${env:whatever, 2}
-
TOML, YML, JSON, etc support
Configorama will work on any configuration format that can be converted into a JS object.
Parse any config format and pass it into configorama.
-
Configorama has a number of built-in functions.
Build in functions can be used within expressions as another way to transform and combine values. These are similar to the operators but all follow a common syntax:
<FUNCTION NAME>(<ARGUMENT 1>, <ARGUMENT 2>)
example:
${merge('one', 'two')} => 'onetwo'
This is forked out of the serverless framework variable system.
Mad props to:
erikerikson, eahefnawy, HyperBrain, ac360, gcphost, pmuens, horike37, lorengordon, AndrewFarley, tobyhede, johncmckim, mangas, e-e-e, BasileTrujillo, miltador, sammarks, RafalWilinski, indieisaconcept, svdgraaf, infiniteluke, j0k3r, craigw, bsdkurt, aoskotsky-amplify, and all the other folks who contributed to the variable system.
Additionally these tools were very helpful: