Dynamic settings in config.json from environment - javascript

In my config.json, I have a requirement for a setting object to be populated dynamically based on the environment variable setting.
"somesetting": {
"setting1": "%S1%",
"setting2": "%S2%"
}
My environment is Windows.
Many examples which I have come across have hard coded values and I am unable to understand how to handle my scenario. I am very new to npm and the javascript world. Any advice / references in this matter is really appreciated
Edit 2:
Possibly,I missed it, but my situation is that values of the %S1% and %S2% could be different in different environments. Consider S1 and S2 as some paths which are likely to be different based on which machine my code builds on the cloud e.g. C:\xyz or D:\xyz etc, which I would have no way of knowing upfront. So this means I can't have a static setting in it even if my environments are different.
Thanks

JSON is a static file and its content is a string, so you can't use variables here.
You can try to rewrite json file via javascript using fs module readFile and writeFile.
Maybe template engines like Handlebars and Mustache will help you to do that much easier.
But usually developers use one static file for one environment. You can change config only before starting your application, otherwise you can break it in a runtime (so bad for your users and customers).
Have a look at this
https://www.npmjs.com/package/config
It's JSON-based config.
We use this package for many years and it's quite good for a few environments.
Infrastructure
config/
- default.json
- development.json
- staging.json
- production.json
app.js
*/
Usage
/* default.json */
{
"HTTP": {
"PORT": 3001,
"MESSAGE": "Hello!"
}
}
/* development.json */
{
"HTTP": {
"PORT": 4001
}
}
/* app.js */
const config = require('config');
const message = config.get('HTTP.MESSAGE');
// "Hello!" (from default.json)
const port = config.get('HTTP.PORT');
// 4001 (from development.json, overrides 3001)
/* Windows */
set NODE_ENV=development& app.js
/* Linux */
NODE_ENV=development app.js
/* cross-platform */
cross-env NODE_ENV=development app.js

dotenv is a common solution for managing environment variables in node.js.
You can define an .env file for each environment you need. Then access them as node environment variables.
Depending on your use case, you could use environment variables to populate your config.json, or perhaps you won't need the config file at all and can just use the environment variables directly.

If I understand you correctly, you want to use Windows environment variables in your Node.js application so that the values are coming from the host operating system/environment itself rather than being hard-coded in a config file, which is a normal CICD requirement. Windows environment variables will be available wherever Node.js is installed by default. You don't need any additional packages. Both system and user environment variables will be available in Node.js process environment, process.env.whatever. For example:
const setting1 = process.env.setting1
const setting2 = process.env.setting2
However you cannot use them inside a .json file because they are static files, as #max-starling said. In this case I think a JavaScript config file makes more sense.
Main app.js
import config from './config'
console.log(config.setting1)
config.js
const somesetting = {
setting1: process.env.setting1,
setting2: process.env.setting2
}
module.exports = somesetting;
Node docs: https://nodejs.org/api/process.html#process_process_env

Related

What's the difference between exposing environment variables in nextjs through the next.config.js vs with the NEXT_PUBLIC prefix?

According to the nextjs documentation, if I want to expose my environment variables to the browser I can just prefix them with NEXT_PUBLIC in my .env.local file, like so:
NEXT_PUBLIC_VAR=7
However, it looks like I can also expose my environment variables to the browser by using next.config.js, like so:
module.exports = {
env: {
PUBLIC_VAR: process.env.PUBLIC_VAR,
},
}
And this will add to the javascript bundle
What is the difference between these two strategies?
NEXT_PUBLIC is a new feature added. Before, in order to set up environment variables, we had to set up both server and client, separately.
Environment variables that are placed in the .env file would be available only on the server-side, if you want to make your env variables available on the client-side you had to use next.config.js. We follow the separation of concerns principle here.
But setting env variables for the browser in the next.config was too much unnecessary typing. This was an example in next.config.js for the client side env:
module.exports = {
env: {
AUTH0_NAMESPACE: process.env.AUTH0_NAMESPACE,
BASE_URL: process.env.BASE_URL
}
}
With NEXT_PUBLIC, env variables will be available both client-side and server-side. env variables that are set with NEXT_PUBLIC will be exposed to the browser. So make sure that you do not expose sensitive data to the browser.
So in summary, adding the prefix NEXT_PUBLIC to your environment variables will have the same effect of exposing to the browser your environment variables as exposing them through next.config.js.
try this:
place this to .env or env.development file. NOT to next.config.js
MY_VAR=10
then run this:
console.log("MY var",process.env.MY_VAR);
both inside client component and getServerSideProps function.
if you check the browser console, you will get undefined, but on terminal, you will see
MY var 10
The difference between to the two is the one of them uses a .env file whereas the other uses the next.config file. Since Next.js 9.4, loading environment variables with .env files are now supported.
However, to clarify one thing in your question, all environment variables within the .env file don't have to be prefixed with NEXT_PUBLIC, only the ones you want exposed to the browser, any without that prefix will only be accessible on the server.

How to specify env variables in node app?

It's a lot of references to dotenv library to use when you want to specify env variables. But why? I can just specify my var in a file like this:
var dev = {
}
var prod = {
}
var config = null;
if (process.env.NODE_ENV === 'production') {
config = prod
} else {
config = dev
}
exports.config = config
and assign my var in npm srcipts, like this:
"scripts": {
"start": "NODE_ENV=dev node bin/dev",
"production": "NODE_ENV=production node bin/production"
}
Is my way not secure? Why is dotenv way recommended? Why should I create .env files instead my config.js?
Environment variables is the commonly assumed way to configure the behaviour of a program, across all programming languages. It is also supported out-of-the box in most CI/CD tools, as well working really well with the command line.
In your example, you assume that the complete config of the prd environment will be stored in the config, including db password etc. It is not considered secure to store any secrets in source code.
The.env file is a common utility for bundling environment variables. It is really is easy to create a .gitignore file with this pattern that prevents it from ever being committed so that configuration stays local. Note that the consumer of the package doesn't have to use a .env file but could also have global/local environment vars where the script is ran. Development solid and not so prone to mistakes.
Syntax simplicity. instead of creating an ad-hoc source code file containing configuration, with more complex syntax than key=value and less common to understand.
#Nastro, I'll point a little and simple different approach.
Develop your application 100% agnostic of an environment. In other words, keep away *from versioning files within your code or lots of if assigning different values to globals, sessions attributes and etc.
Favor your environments with the due env vars and values. Usually, the most strategic or special environments will be protected against unwanted access(production, staging and etc), so your secret values will be unreachable.
A single db_password = process.env.DB_PASS will be reusable for any existing and future environment you or your team creates.
This is a simple, yet effective approach, but demands a minimal control over your environment and hosts.

Conditional javascript source code

Background:
I have 3 different URLs, one per environment (dev, test, prod), and I don't want to expose all the URLs in the client (source code).
How can I expose in the client code, just the one corresponding to the environment in context?
Note: As I understand, I need to do something in the build process using environment variables (I'm using node.js). However, I don't want to touch anything related with webpack, as what I'm trying to do is a standalone package that can be imported in any application regardless of the framework they are using. Webpack plugins/configuration are not an option, but I can use any npm package if required.
During your build process, you can check the environment variable and then copy over a config file. For example, you could keep your URIs in /config/<env>.js, and then copy/rename it to /settings.js during the build. Your URL could be exported from that.
The following npm package fits my requirements completely https://www.npmjs.com/package/config , you can load conditional files based on the node environment variable NODE_ENV, so when NODE_ENV=development, the file /config/development.js is used to create the build. you can use different extensions for the config files, also you can customize the config folder path by changing the environment variable $NODE_CONFIG_DIR heres an example:
const config = require('config');
process.env.$NODE_CONFIG_DIR = './' // relative path ./config
const url = config.get('url');
//if NODE_ENV is development will load the file config/development.js
console.log(url);

Managing JavaScript config files for different build environments (like QA, UAT, Production)

So you are building a stand alone Single Page Application in JavaScript that is served statically. It connects to an API.
Your application has a JSON object in its main.js like this:
var config = {
apiPath: 'http://dev.api.example.com',
appTitle: 'hello'
};
You use Grunt to create a release build, this transpiles all your SASS, ES6, AMD Modules etc code into one or two JavaScript files, along with this config object.
Your production API url is http://api.example.com and when you do a release build, you want just this value in the config to change. (Maybe you would want to change other config parameters too). You have a release config
var config = {
apiPath: 'http://api.example.com'
};
How can a JavaScript config file be altered depending on different types of release environments during a build. Notice that I want to merge the release config into the default, not maintain totally seperate files with all the settings. While there are grunt plugins for setting a build mode, I've not found any that have the ability to update config files, only hacks that change which file is pointed to.
ASP.NET has web.config transforms that do exactly this, any property in the release transform for example, will overwrite the 'default' config on packaging. Does such a workflow exist using grunt or something?
In the end I solved the question myself by using a combination of environment variables and an amazing Node called Confidence which where you place all your different envs config settings in a single file, and they are resolved to a single configuration based on filters (such as an environment variable) - these can then be injected into the build process using tools like WebPack.

Node.js: How to setup different variables for prod and staging

I'm using Express and I need to use different credentials for each server (staging and production).
I could setup the variables in the server.coffee file but then I'd need to access those variables in different files.
server.coffee:
app.configure 'production', () ->
app.use express.errorHandler()
What's the solution? Setup the variables and then export them?
./config.js
var development = {
appAddress : '127.0.0.1:3000',
socketPort : 4000,
socketHost : '127.0.0.1',
env : global.process.env.NODE_ENV || 'development'
};
var production = {
appAddress : 'someDomain.com',
socketPort : 4000,
socketHost : '8.8.8.8',
env : global.process.env.NODE_ENV || 'production'
};
exports.Config = global.process.env.NODE_ENV === 'production' ? production : development;
./app.js
var cfg = require('./config').Config;
if (cfg.something) { // switch on environment
// your logic
}
This might be a good place to use npm-config.
When running scripts (see npm-scripts(7)) the package.json "config" keys are overwritten in the environment if there is a config param of <name>[#<version>]:<key>
I would not use them for every type of variable configuration setting, but I think it's a good solution for simple cases like URLs and ports because:
You put them directly into package.json.
In addition, you can specify them on the command line or as ENV variables.
Anything run through npm can refer to them (e.g., scripts).
You can set them per-user with `npm config set foo:port 80
The one caveat is that the config parameter in your package.json is only automatically exported into the environment when you run your code through npm. So, if you just run it with node, like, node ./myapp.js, then you can't expect that process.env.npm_package_config_foo will contain your value. However, you can always var pkg = require('./package.json'); and access the values at pkg.config.
Because it might not be immediately obvious, I'd also add that the npm_package_config environment variables do not bubble up to apps that depend on your npm package. So, if your depended-on package refers to process.env.npm_package_config_foo, then the dependent package would have to define that in its own package.json. I guess since it's an "npm_package_config" it wouldn't make sense to push them all the way up the tree.
So, how would I use one npm config variable and have it work all the way up the tree, in both the base package and the packages that depend on it? It's actually a little confusing, and I had to figure this out through trial and error.
Let's say you have a package connector and package client. client depends on connector and you want to specify a config parameter for connector that can be used or overwritten in client. If you use process.env.npm_package_config.port in your connector module, then when it's depended on in client module, then that variable won't be exported and it will end up as undefined.
However, if you instead use process.env.npm_config_connector_port (notice the first one starts with npm_package_config and the other with npm_config_packagename), then you can at least set that in your .npmrc using npm config set connector:port 80 and it will be "namespaced" as process.env.npm__config_connector_port everywhere that you run npm, including scripts that you run in client that depend on connector, and you'll always be able to overwrite it on the command line, in your ENV, or in your .npmrc. You just have to keep in mind that, as with any environment variable, it may not always be set. So, I would use the default operator with the process.env.npm_config_connector_port as the first (preferred) value:
var port = process.env.npm_config_connector_port || sane_default
Here, sane_default could be populated from one of the other recommended methods. Personally, I like keeping configuration data like these in JSON files at the very least, and package.json seems like the best JSON file to put them in. Store them in data instead of code and then you can easily use the static JSON in-line, generate them dynamically, or pull them from the filesystem, URLs or databases.
I have uploaded an implementation into https://github.com/qiangyu/nodejsconfig. I believe it will satisfy your needs. Basically, you only need to provide one configuration file:
dev.appAddress = '127.0.0.1:3000';
prod.appAddress = 'someDomain.com';
Then, you use following code to read the appAddress in prod environments:
var xnconfig = require('nodejsconfig');
var fs = require('fs');
var data = fs.readFileSync(__dirname+"/config.properties", "UTF8");
// assume we will be using environment "prod"
var config = xnconfig.parse("prod", data);
// the output will be someDomain.com
console.log(config.getConfig("appAddress"));
If you don't want the logic for determining which config to use in each file (which would look pretty ugly), you'll have to export it somewhere.
What I would suggest: Have a config.json file containing the different configs. The main file requires it and does something like config.default = config.(condition ? 'production':'development'). In all other files, you can now just do require('./config').default.
I have an app which uses three different methods of declaring config variables (uris, api keys, credentials, etc.) depending upon the environment (production = environment variables; staging = command line args; local = config files.)
I wrote a little "config" module to handle merging all of these options into one object that I can use in my app and uploaded it as a gist: https://gist.github.com/1616583
It might not be the best implementation, but it's been working pretty well so far :).

Categories

Resources