Manage project-wide configuration from an immutable in-memory store, using a friendly interface powered by lodash's get/set path-like selectors.
I find myself writing the same module in different projects, and the libraries I found are overkill for what I need, so I decided to distribute it as a small package.
And it goes pretty well with merge-files-content Node.JS module!
Before installing, you need to install lodash by yourself, since it's a peerDependency.
In case your project doesn't already uses it, run npm i lodash
.
npm i memconfig
or
yarn add memconfig
This module works both in the browser and Node.JS environments. Let's see some examples:
const { Config } = require('memconfig')
const config = new Config()
config.set('database.port', 3001)
config.get('database').port === 3001 // true
config.get(['database', 'host'], 'default-host') // "default-host"
import { Config } from 'memconfig' // es6 supported!
const config = new Config()
// user enables dark mode
config.set('userSettings.darkMode', true)
// Then you have to persist local settings
localStorage.setItem('ui_config', config.toString())
// The config can be deserialized with the from() method
const config = Config.from(localStorage.getItem('ui_config'))
config.get('userSettings') // {"darkMode": true}
The Config
objects accepts an object of settings:
new Config({
/**
* If true, every object is going to be clonned in order
* to prevent side-effect mutations
*/
immutable: true,
})
Use config.setStore(yourOwnObject)
to initialize it with your app defaults.
Freeze the Config instance and prevent new values to be inserted into the store. It shall throw an Error
if config.set()
is called while config.frozen
is true
.
Use config.unfreeze()
to unfreeze.
β οΈ For Node.JS, it is advisable to use this method once your app starts if you're clustering it, to ensure that nobody tries to mutate configurations at runtime.
Internally it uses lodash's get to resolve valuePath
keys.
Internally it uses lodash's set.
Merges the given object (or instance of Config
) into the store.
Internally it uses lodash's unset.
Same as doing JSON.stringify(config.store)
.
Static method to create an instance of Config
out of a "stringified" store.
Accepts a second parameter to pass settings to the constructor.
As simple as setting object properties should be, these methods are not linear. That means your program will iterate over many objects when you use valuePaths like level1.level2
or ['level1', 'level2', 'level3']
to access some value in the store
So always try to cache the first levels of the objects you need at module level, and use the values as a normal object. For instance:
DO:
const config = require('../config')
const databaseConfig = config.get('database', { port: 3001 })
const getDatabasePort = () => databaseConfig.port
DON'T:
const config = require('../config')
const getDatabasePort = () => config.get('database.port', 3001)
Since we're talking about in-memory stores, each instance of your clustered Node.JS app will have its own instance of the Config object, hence you shouldn't use set()
anymore after the fork()
is done, because the forked processes won't see their config objects updated.
If that's a problem for you, an in-memory store may not be what you're looking for but something like a KVS or such things.
So the implementation should be:
const config = require('./config')
const { config: database } = require('./modules/database')
const { config: storage } = require('./modules/storage')
const app = require('./app')
config.setStore({ database, storage })
// Freeze the store to throw an error if somebody tries to set() something at runtime
config.freeze()
app.start() // now create you app's cluster