Having a dynamic NodeJS configuration allows software systems to change their configuration at runtime. Though the benefits of this are legion, it may not be obvious at the start.Here at Chapter247 Infotech, we ensure that our Node.js applications are dynamically configured for easier implementation of authorization protocols, feature flags, and A/B testing procedures

Let’s dive deeper and learn how dynamic configuration gives a definite edge to the Node.js applications here at Chapter247 Infotech.

Variations of Dynamic NodeJS configuration
Dynamic or runtime configuration can be broadly divided into two categories:

  • Using an outside source to feed the new configuration data to the application
  • Using a catalyst to change the application’s existing config

Usually, a dynamic configuration is characterized by the first case, i.e. using an external source file to feed the new configuration details to the application. This is considered to be a convenient feature, especially if the application works on a distributed system architecture.

However, to unleash the full power of dynamic configuration, we must adapt to the second factor as well. This principle assumes that there are multiple configuration data values for a particular data point. During runtime, the application selects a particular configuration value from the available range based on calculations influenced by external entity interactions.

Utilizing both types of dynamic configuration leads an application to enable the use of:

  • Complex authorization protocols
  • Feature flags, to ensure safe, incremental releases.
  • A/B testing
  • Discovery of services
  • Dynamic routing
  • UX customization strategies

The above-listed items are but a subsection of an exhaustive list of what dynamic configuration can help us achieve. All these features are driven by configuration data that varies in response to certain catalysts like:

  • Engineers who modify configuration specs in a data store and push relevant changes to the system.
  • The application’s runtime environment which necessitates a config change in-line with the changes to its state
  • End users who affect the config changes as per their use.

Dynamic configuration can affect certain characteristics of the system as follows:
Authorization
End user characteristics affect the configuration data required for authorization logic.

Feature flags
End user actions or pushes to the codebase by engineers affect features that are wrapped in logic using configuration data.

A/B testing
To test certain features or metrics that use logic dependent on the config data.

Service discovery
Service endpoints are sourced from config data which is affected by changes to the codebase by engineers or changes to the runtime environment.

Dynamic Routing
Reverse proxies use configuration data that is dependent on updates to the codebase or runtime environment changes

Custom UX
Different users have different experiences while using the system which depends on config data-dependent logic influenced by user characteristics.

Dynamic configuration techniques can make life a whole lot easier while implementing these features.

Let’s understand how Chapter247 Infotech uses flipr to tackle these scenarios

Using Flipr for Dynamic configuration

Flipr is a library in Node.js supporting both static and dynamic configuration. It was created back in 2015 and has been used by several organizations like GoDaddy in production environments, since its release.

Flipr obtains config data from a source and then exposes it to the application using a simple interface. Applications can choose the retrieve configuration details via individual keys or in bulk. Application-specific rules can also pass inputs to Flipr for enabling dynamic configuration.

The below snippet uses a simple static configuration example to showcase Flipr’s components. The file is used as the data source and it reads config data from yaml files which exist parallel to application code and feeds it to Flipr

const Flipr = require('flipr');
const FliprYaml = require('flipr-yaml');
const configuration = new FliprYaml({
filePath: '',
});
const fliprConf = new Flipr({
configuration,
});
fliprConf.getConfig().then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
or
async/await syntax:
try{
const result = await fliprConf.getConfig();
console.log(result)
}catch(e) {
console.log(e)
}

This code defines a basic yaml config file, setups flipr to read it and retrieves the value of the config item in the database server. This helps highlight the fact that Flipr retrieves config data asynchronously even if the source can retrieve the data through synchronous actions. This is done for ensuring compatibility.

The below snippet provides a simple example of dynamic configuration focussing on changing existing configuration through a catalyst. This particular example changes the database server of the application based on user ID.

const Flipr = require('flipr');
const FliprYaml = require('flipr-yaml');
const flipr = new Flipr({
source: new FliprYaml({
filePath: ''
}),
rules: [
{
type: 'equal', // determine how rule compares input to rule property.
input: 'id', // the path of the input to compare, i.e. input.id (this also supports nesting like input.user.name or input.user.id)
property: 'id', // the name of the rule property in the configuration
}
] });
const user = {
id: 145,
};
const newUser = {
id: 890,
};
try{
const userResult = await flipr.getValue("databaseServer", user1);
console.log(userResult)
}catch(e){
console.log('error in first user', e)
}
try{
const newUserResult = await flipr.getValue("databaseServer", user2);
console.log(newUserResult)
}catch(e){
console.log("error in newUser", e)
}

Though these examples are for demonstrative purposes only, they are sufficient to showcase Flipr’s ability to return multiple config values based on rule-based inputs from the application.

Flipr functionality in common scenarios

Dynamic configuration encompasses several features as discussed earlier in the document. Let’s take a more detailed look at some of them.

Authorization

Authorization takes a Boolean Yes or No decision based on a user’s granted access permissions. Flipr can make this easier through the definition of authorization points in the config and using them to make those decisions

const Flipr = require('flipr');
const FliprYaml = require('flipr-yaml');
const flipr = new Flipr({
source: new FliprYaml({
filePath: ''
}),
rules: [
{
type: 'equal',
input: (input) => {
return input.user.role.toString().toLowerCase() === 'admin';
},
property: 'isAdmin'
}
] });
const userInput = {
user: {
role: 'admin'
}
};
try{
const result = await flipr.getValue("databaseServer", userInput);
console.log(result)
} catch(e){
console.log(e)
}

In general, it is better to define authorization criteria in the config itself rather than creating separate configuration items.

Feature Flags

Feature flags operate similar to authorizations, but in the case of features. They help enable or disable features in the application. Flipr allows you to have your feature flags respond dynamically based on user context. This helps to test out features to a chosen set of testers before a major release or to disable some features based on the environment in question.

const Flipr = require("flipr");
const fliprConfig = new Flipr({
source: require(""),
rules: [
{
type: "equal",
input: function(input) {
return input.user.role.toString().toLowerCase() === "admin";
},
property: "isAdmin"
}
]
});
const users1 = {
user: {
role: "manager"
}
};
const users2 = {
user: {
role: "admin"
}
};
const features = async () => {
try {
const configUser1 = await fliprConfig.getConfig(users1);
console.log(configUser1)
const configUser2 = await fliprConfig.getConfig(users2);
console.log(configUser2)
} catch (e) {
console.log(e);
}
}
try {
await features();
} catch (e) {
console.log("error in calling feature function", e)
}

A/B Testing

A/B tests enable the testing of different functions for different sets of users and understand the user’s response based on recorded metrics. Though Flipr isn’t a comprehensive A/B testing tool, it does more than a decent job in this regard.

const Flipr = require("flipr");
const fliprConfig = new Flipr({
source: require(""),
rules: [
{
type: "equal",
input: user => Flipr.idToPercent(user.id, "isPassed") <= 0.5 ? "a" : "b",
property: "passed"
}
]
});
user.abTests.push(await fliprConfig.getValue("isPassed", user));
const testResult = await flipr.getValue("isPassed", user)
if (testResult== "one") {
// do your stuff here
} else if(testResult == "two") {
// do your stuff here for two
} else {
// do your stuff for else no condition match
}

Service Discovery

The previous examples showcase the change of existing config values based on catalysts. However, service discovery focusses on receiving and using new configuration data. This is achieved by using a Flipr source that automatically receives updates from external sources.

const Flipr = require("flipr");
const fliprConfig = new Flipr({
source: require(""),
});
const url = await fliprConfig.getValue('endpoint')
const response = await fetch(url);
console.log(response.json());

Some best practices

Flipr can be quite a handful if its flexibility isn’t handled in the right way. Some ways to keep the code and configuration maintainable using Flipr are:

  • Creating one Flipr instance per source to optimize Flipr’s internal caching.
  • Allowing config files to exist parallel to application code and validate said files through unit tests. However, this may be tuned based on the CI/CD pipeline.
  • Creating separate config files based on the environment and its purpose.
  • Documenting the configuration files.
  • Cleaning up stale configurations at periodic intervals.

Conclusion

Node.js has garnered quite a reputation as a top platform for building web applications especially online shopping carts and ECommerce platforms due tis lightweight nature and scalability. Dynamic NodeJS configuration helps to further bolster its usefulness by providing greater ease in implementing authorization protocols, feature flags and performing A/B testing

Chapter247 Infotech, has a stellar track record in Node.js web development and its customer-centric approach in custom software development, has put it at the very forefront. Our dedicated team of professionals work closely with you to offer bespoke applications through custom Node.js web development with dynamic configuration.

Contact us to build a highly personalized ecommerce marketplace using the power of Node.js.

Share: