appcfg

Application Configuration Library for Node.js

Load and Combine Configuration from many sources:

  • Files
    • JavaScript Module
    • JSON
    • Yaml
  • Directory (combine files)
  • MongoDB

Additional features:

When combining objects from many sources, deep copy is used:

// a 
{ sub: { foo: 'foo' }, arr: [ 'foo' ] }
// b 
{ sub: { bar: 'bar' }, arr: [ 'bar' ] }
 
// combined (a + b) 
{
    sub: { foo: 'foo', bar: 'bar'},
    arr: ['foo', 'bar']
}
 
// E.g you want completely to overwrite `arr` property, then prefix the key's name with `!` 
 
// a 
{ sub: { foo: 'foo' }, arr: [ 'foo' ] }
// b 
{ sub: { bar: 'bar' }, '!arr': [ 'bar' ] }
 
// combined (a + b) 
{
    sub: { foo: 'foo', bar: 'bar'},
    arr: ['bar']
}

Example:

    var config = Config
        .fetch([
            // from file. (with Special Folder format syntax support) 
            {
                path: '%APPDATA%/.appName/config.yml',
                
                // set this source as writable for configuration persistance 
                writable: true
            },
            // directory 
            {
                path: 'defaults/**.yml'
            },
            // mongodb 
            {
                mongo: 'barSettings'
            },
            // from file, but use only nested property 
            {
                path: 'package.json',
                getterProperty: 'atma'
            }
        ])
        .done(function(cfg){
            // config === cfg === this; 
            // ... 
        })

Command line arguments are parsed and also set to the configuration object.

> node app --foo.bar barValue --debug
Config
    .fetch(someSources)
    .done(function(config){
        assert.has(config, {
            foo: {
                bar: 'barValue'
            },
            debug: true
        })
    })

Yaml conditions example. (same is also for json format)

# conditional root configuration example
'#if debug':
    name: Foo
    host: dev.example.com
'#if test':
    name: Baz
    host: localhost
 
# conditional property example
port: 
    '#if debug': 5000
    '#if test': 5030
    'default': 8080
 
# conditional array item example
scipts:
    - lib.js
    - '#if debug || test'
        - lib.debug-extension.js
    

Arguments lookup:

  • in configuration object
  • in cli overrides
  • in process.env
  • compare value as string from process.env.ENV
# from cli example
> node app --debug
 
# from environment
> set ENV=DEBUG
> node app
{
    name: 'Foo',
    host: 'localhost',
    port: 5000,
    scripts: [
        "lib.js",
        "lib.debug-extension.js"
    ]
}

Use special folders for loading/writing configurations, like %APPDATA% or %HOME%. Is system agnostic and is parsed from the environment variables.

Config
    .fetch({
        path: '%APPDATA%/.myApplication/global.yml',
        writable: true
    })

Sometimes to not repeat the configuration it is convinient to use interpolations. It will embed configuration from itself.

# someConfig.yml
name: 'Foo'
A:
    lorem: '#[name]'
B:
    ipsum: '#[A.lorem]'
Config
    .fetch({
        path: 'someConfig.yml'
    })
    .done(function(config){
        assert.has(config, {
            name: 'Foo',
            A: { lorem: 'Foo' },
            B: { ipsum: 'Foo' }
        });
    })

static

Start loading the configuration from specified sources, returns new deferrable configuration instance

methods

  • .$read(?Mix)

    • Mix:
      • String: File/Directory/Glob path
      • Source: Source object
      • Array<Source>
      • @default - Array taken from constructor
    • @return: self. Is deferrable, so you can attach done callbacks to it.

    Load configuration from sources

  • .$write(config:Object [, ?deepExtend:Boolean, ?setterPath:String):Deferred

    Update and save the configuration. Use first matched writable source.

    • deepExtend: complex objects and arrays are merged
    • setterPath: define nested object in current configuration
  • .$get(property:String)

    • property: Dot delimited accessor config.$get('foo.bar.quux')

      // is the same as you if you would write null-check expressions yourself: 
      var quuxVal = config.foo && config.foo.bar && config.foo.bar.quux;
    • @return: null or value

  • .$is(name:String):Boolean

    Check conditional environment variable, e.g.: config.$is('debug')

    Ways to define the variables. (Example defines DEBUG and TEST flags)

    • directly in the configuration
      // foo.yml
      debug: true
      test: true
    • from the command line:
      > node index --debug --test
    • using environment configuration(comma delimited)
      > set ENV=DEBUG,TEST
      # also
      > set NODE_ENV=DEBUG,TEST
      > node index
  • .toJSON():Object

    Returns clean json configuration object.

  • .done(callback)

    Fire the callback when the configuration is loaded

{
    // Define specific property to extract SUB-JSON from the loaded configuration 
    // @default: null 
    getterProperty: String
    
    // Define specific property in the root configuration, 
    // where the loaded configuration should be inserted into 
    // @default: null 
    setterProperty: String
    
    // Specify if this source can be used for persistence 
    // @default: false 
    writable: Boolean
    
    // Fires before source $read function is called 
    // (e.g. change this.path property or any other things) 
    beforeRead: Function<Source, RootConfig>
    
    // Fires after source completes reading 
    // (e.g. access config object in `Source.config`) 
    afterRead: Function<Source, RootConfig>
    
    // If true, do not log any warning if the source returns 404 
    // @default: false 
    optional: true
    
    // If true, then waits until all previous sources are loaded 
    // @default: false 
    sync: true
}
{
    // File path 
    path: String
}

It will be mapped to multiple FileSources

{
    // Directory path with GLOB look-up, e.g. 'configs/**.json' 
    path: String
}

Depends on ClassJS

{
    // Collection name 
    mongo: String,
    
    // if source is writable 
    // @default: true 
    writable: Boolean
    
    // MongoDB Connection Settings 
    // It can be also specified in previous configuration source, under `mongodb` property 
    // @default: null -  
    settings: {
        // connection string 
        connection: String
        // or 
        
        // Port, default 27017 
        port: Number,
        // IP, default '127.0.0.1' 
        ip: String,
        
        // Database name, no default 
        db: String
    }
}

This source type can suit any needs.

 
// Constructor with the Deferrable Interface and the method `read` 
Function 
 
// e.g. using ClassJS 
 
Class({
    Base: Class.Deferred,
    readfunction(){
        // do any reads and calcs, after that resolve the source 
        this.config = fooConfig();
        this.resolve();
    }
})
 

Include config direct into the source

{
    config: Object
}
> npm install
> npm test

(c) 2014 MIT License