Alternative for .env file for hosted Node.js application - node.js

I try to find a way to get rid of .env file from dev, stage and prod environments for Node.js application.
The current solution which I have seems to be a little bit factitious. During the build process, in pipeline env variables are rewrite from Jenkins (Config File Provider) into new .env file which is attached to an application. The deployed application in run by pm2.
I wonder if is it a better approach to run ${envlist} pm2 start app?

this is just an option that I use for handling the deployment. In my projects, I always have a file ecosystem.config.js
module.exports = {
apps: [{
name: "my little project",
script: "./app.js",
instances: "max",
env: {
NODE_ENV: "production",
},
env_development: {
NODE_ENV: "development",
},
env_production: {
NODE_ENV: "production",
}
}]
}
in my entry point I have (app.js):
if (process.env.NODE_APP_INSTANCE === '0' || process.env.NODE_ENV !== 'production') {
// start cronJobs here or any action needs to be done in single proccess
}
if (process.env.NODE_ENV === 'production') server.listen(800 + process.env.NODE_APP_INSTANCE);
else if (process.env.NODE_ENV === 'test') server.listen(700 + process.env.NODE_APP_INSTANCE);
else
server.listen(3045, err => {
console.log(err ? err : `REST server started on 3045.`);
});
and in your jenkins file :
stage('Deploy') {
environment {
NODE_ENV = "production"
}
steps {
sh 'pm2 start ecosystem.config.js'
}
}
you can run your app with any env you want:
pm2 start ecosystem.config.js --env production

Related

vite dev server execute middleware before all other middleware

With vue-cli it was possible to configure webpack devServer.before function like this:
devServer: {
before(app) {
app.get('/apiUrl', (req, res) => res.send(process.env.API_URL))
}
},
How is it possible to configure Vite dev server to obtain the same behavior?
(I tried with the proxy option but it does not work.)
According to this github issue, environment variables are not accessible in file vite.config.js (neither in vite.config.ts). However, the discussion in this issue also mentions a workaround that you can use in this file:
import { defineConfig, loadEnv } from 'vite'
import vue from '#vitejs/plugin-vue'
export default defineConfig(({mode}) => {
const env = loadEnv(mode, process.cwd());
return {
plugins: [
vue(),
],
server: {
proxy: {
'^/apiUrl': {
target: env.VITE_API_TARGET,
changeOrigin: true,
}
}
},
}
})
Note that the variable name must start with VITE_ for this to work.

react-boilerplate yarn start:prod This site can’t be reached localhost refused to connect. http://localhost:3000 => https://localhost/

https://github.com/react-boilerplate/react-boilerplate
Description
After running yarn run build,
yarn start:prod
It says it is running on the terminal window, however,
when i go to http://localhost:3000 the url suddenly changes to => https://localhost/ and says
this site can’t be reached localhost refused to connect.
development mode yarn start works fine
Steps to reproduce
I removed ImmutableJS following the guide from one of the issues in react-boilerplate.
I added feathersJS backend, frontend.
I changed babel-loader in webpack.base.babel.js
to
rules: [
{
test: /\.js$/, // Transform all .js files required somewhere with Babel
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: options.babelQuery,
query: {
plugins: [
["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
],
},
},
},
I changed the app.js file
// Install ServiceWorker and AppCache in the end since
// it's not most important operation and if main code fails,
// we do not want it installed
if (process.env.NODE_ENV === 'production') {
// require('offline-plugin/runtime').install(); // eslint-disable-line global-require
const runtime = require('offline-plugin/runtime');
runtime.install({
onUpdating: () => {
console.log('SW Event:', 'onUpdating');
},
onUpdateReady: () => {
console.log('SW Event:', 'onUpdateReady');
// Tells to new SW to take control immediately
runtime.applyUpdate();
},
onUpdated: () => {
console.log('SW Event:', 'onUpdated');
// Reload the webpage to load into the new version
window.location.reload();
},
onUpdateFailed: () => {
console.log('SW Event:', 'onUpdateFailed');
}
});
}
Much appreciate your help!
(Add link to a demo on https://jsfiddle.net or similar if possible)
Versions
React-Boilerplate (see package.json): 3.6.0
Node/NPM: v9.11.1
Browser: chrome
I forgot I was using ssl-redirect for heroku deployment.
var sslRedirect = require('heroku-ssl-redirect');
// heroku enable ssl redirect
app.use(sslRedirect()); //heroku https
cheers :)

Injecting an environment variable from gulp to webpack

I have a gulpfile from which I'm initiating webpack bundling. The webpack configuration has an alias defined that uses process.env.NODE_ENV as shown below
.
.
resolve: {
modulesDirectories: ["node_modules", "js", "jsx"],
extensions: ["", ".js", ".jsx"],
alias: {
config: path.join(__dirname, ('config.' + process.env.NODE_ENV + '.js'))
}
}..
In the gulpfile, I have 2 tasks that execute in sequence
set-env that sets the environment variable using gulp-env as follows
gulpEnv({
vars: {
NODE_ENV: 'dev'
}
});
webpack task that depends on the previous task and executes only when the first is completed
Here the variable is not getting injected properly it seems. The file name is getting resolved as config.undefined.js. Is there something wrong with what I'm doing?
I changed my approach it to something like this
Webpack
var NODE_ENV = process.env.NODE_ENV;
var config = {
.
.
resolve: {
modulesDirectories: ["node_modules", "js", "jsx"],
extensions: ["", ".js", ".jsx"],
alias: {
//<envt> will be injected by gulp
config: config: path.join(__dirname, ('config.<envt>.js'))
}
}
.
.
.
}
//If webpack is directly invoked with NODE_ENV, replacement will happen here
if (NODE_ENV) {
config['resolve']['alias']['config'] = config['resolve']['alias']['config'].replace(/<envt>/, NODE_ENV);
}
module.exports = config;
Gulp
Inside set-env instead of setting using gulp-env, I simply set a global variable NODE_ENV='dev'
Inside webpack task, I'm doing webpackConfig['resolve']['alias']['config'] = webpackConfig['resolve']['alias']['config'].replace(/<envt>/, NODE_ENV);
This worked for me

Running node app from grunt with watch

So, I have the grunt file below. I'm wanting to add a task that will start my node app and watch for changes in a directory and restart. I have been using supervisor, node-dev (which are great) but I want to run one command and start my whole app. There has got to be a simple way to do this, but I'm just missing it. It is written in coffeescript as well (not sure if that changes things)...
module.exports = function(grunt) {
grunt.initConfig({
/*exec: {
startApi: {
command: "npm run-script start-api"
}
},*/
//static server
server: {
port: 3333,
base: './public',
keepalive: true
},
// Coffee to JS compilation
coffee: {
compile: {
files: {
'./public/js/*.js': './src/client/app/**/*.coffee'
},
options: {
//basePath: 'app/scripts'
}
}
},
mochaTest: {
all: ['test/**/*.*']
},
watch: {
coffee: {
files: './src/client/app/**/*.coffee',
tasks: 'coffee'
},
mochaTest: {
files: 'test/**/*.*',
tasks: 'mochaTest'
}
}
});
grunt.loadNpmTasks('grunt-contrib-coffee');
grunt.loadNpmTasks('grunt-mocha-test');
//grunt.loadNpmTasks('grunt-exec');
grunt.registerTask( 'default', 'server coffee mochaTest watch' );
};
As you can see in the comments, I tries grunt-exec, but the node command stops the execution of the other tasks.
You can set grunt to run default task and the watch task when you start your node app:
in app.js
var cp = require('child_process');
var grunt = cp.spawn('grunt', ['--force', 'default', 'watch'])
grunt.stdout.on('data', function(data) {
// relay output to console
console.log("%s", data)
});
Then just run node app as normal!
Credit

Node.js setting up environment specific configs to be used with everyauth

I am using node.js + express.js + everyauth.js. I have moved all my everyauth logic into a module file
var login = require('./lib/everyauthLogin');
inside this I load my oAuth config file with the key/secret combinations:
var conf = require('./conf');
.....
twitter: {
consumerKey: 'ABC',
consumerSecret: '123'
}
These codes are different for different environments - development / staging / production as the callbacks are to different urls.
Question: How do I set these in the environmental config to filter through all modules or can I pass the path directly into the module?
Set in env:
app.configure('development', function(){
app.set('configPath', './confLocal');
});
app.configure('production', function(){
app.set('configPath', './confProduction');
});
var conf = require(app.get('configPath'));
Pass in
app.configure('production', function(){
var login = require('./lib/everyauthLogin', {configPath: './confProduction'});
});
? hope that makes sense
My solution,
load the app using
NODE_ENV=production node app.js
Then setup config.js as a function rather than an object
module.exports = function(){
switch(process.env.NODE_ENV){
case 'development':
return {dev setting};
case 'production':
return {prod settings};
default:
return {error or other settings};
}
};
Then as per Jans solution load the file and create a new instance which we could pass in a value if needed, in this case process.env.NODE_ENV is global so not needed.
var Config = require('./conf'),
conf = new Config();
Then we can access the config object properties exactly as before
conf.twitter.consumerKey
You could also have a JSON file with NODE_ENV as the top level. IMO, this is a better way to express configuration settings (as opposed to using a script that returns settings).
var config = require('./env.json')[process.env.NODE_ENV || 'development'];
Example for env.json:
{
"development": {
"MONGO_URI": "mongodb://localhost/test",
"MONGO_OPTIONS": { "db": { "safe": true } }
},
"production": {
"MONGO_URI": "mongodb://localhost/production",
"MONGO_OPTIONS": { "db": { "safe": true } }
}
}
A very useful solution is use the config module.
after install the module:
$ npm install config
You could create a default.json configuration file. (you could use JSON or JS object using extension .json5 )
For example
$ vi config/default.json
{
"name": "My App Name",
"configPath": "/my/default/path",
"port": 3000
}
This default configuration could be override by environment config file or a local config file for a local develop environment:
production.json could be:
{
"configPath": "/my/production/path",
"port": 8080
}
development.json could be:
{
"configPath": "/my/development/path",
"port": 8081
}
In your local PC you could have a local.json that override all environment, or you could have a specific local configuration as local-production.json or local-development.json.
The full list of load order.
Inside your App
In your app you only need to require config and the needed attribute.
var conf = require('config'); // it loads the right file
var login = require('./lib/everyauthLogin', {configPath: conf.get('configPath'));
Load the App
load the app using:
NODE_ENV=production node app.js
or setting the correct environment with forever or pm2
Forever:
NODE_ENV=production forever [flags] start app.js [app_flags]
PM2 (via shell):
export NODE_ENV=staging
pm2 start app.js
PM2 (via .json):
process.json
{
"apps" : [{
"name": "My App",
"script": "worker.js",
"env": {
"NODE_ENV": "development",
},
"env_production" : {
"NODE_ENV": "production"
}
}]
}
And then
$ pm2 start process.json --env production
This solution is very clean and it makes easy set different config files for Production/Staging/Development environment and for local setting too.
In brief
This kind of a setup is simple and elegant :
env.json
{
"development": {
"facebook_app_id": "facebook_dummy_dev_app_id",
"facebook_app_secret": "facebook_dummy_dev_app_secret",
},
"production": {
"facebook_app_id": "facebook_dummy_prod_app_id",
"facebook_app_secret": "facebook_dummy_prod_app_secret",
}
}
common.js
var env = require('env.json');
exports.config = function() {
var node_env = process.env.NODE_ENV || 'development';
return env[node_env];
};
app.js
var common = require('./routes/common')
var config = common.config();
var facebook_app_id = config.facebook_app_id;
// do something with facebook_app_id
To run in production mode :
$ NODE_ENV=production node app.js
In detail
This solution is from : http://himanshu.gilani.info/blog/2012/09/26/bootstraping-a-node-dot-js-app-for-dev-slash-prod-environment/, check it out for more detail.
The way we do this is by passing an argument in when starting the app with the environment. For instance:
node app.js -c dev
In app.js we then load dev.js as our configuration file. You can parse these options with optparse-js.
Now you have some core modules that are depending on this config file. When you write them as such:
var Workspace = module.exports = function(config) {
if (config) {
// do something;
}
}
(function () {
this.methodOnWorkspace = function () {
};
}).call(Workspace.prototype);
And you can call it then in app.js like:
var Workspace = require("workspace");
this.workspace = new Workspace(config);
An elegant way is to use .env file to locally override production settings.
No need for command line switches. No need for all those commas and brackets in a config.json file. See my answer here
Example: on my machine the .env file is this:
NODE_ENV=dev
TWITTER_AUTH_TOKEN=something-needed-for-api-calls
My local .env overrides any environment variables. But on the staging or production servers (maybe they're on heroku.com) the environment variables are pre-set to stage NODE_ENV=stage or production NODE_ENV=prod.
Set environment variable in deployment server (ex: like NODE_ENV=production). You can access your environmental variable through process.env.NODE_ENV.
Find the following config file for the global settings
const env = process.env.NODE_ENV || "development"
const configs = {
base: {
env,
host: '0.0.0.0',
port: 3000,
dbPort: 3306,
secret: "secretKey for sessions",
dialect: 'mysql',
issuer : 'Mysoft corp',
subject : 'some#user.com',
},
development: {
port: 3000,
dbUser: 'root',
dbPassword: 'root',
},
smoke: {
port: 3000,
dbUser: 'root',
},
integration: {
port: 3000,
dbUser: 'root',
},
production: {
port: 3000,
dbUser: 'root',
}
};
const config = Object.assign(configs.base, configs[env]);
module.exports= config;
"base" contains common config for all environments.
Then import in other modules like:
const config = require('path/to/config.js')
console.log(config.port)
Happy Coding...
How about doing this in a much more elegant way with nodejs-config module.
This module is able to set configuration environment based on your computer's name. After that when you request a configuration you will get environment specific value.
For example lets assume your have two development machines named pc1 and pc2 and a production machine named pc3. When ever you request configuration values in your code in pc1 or pc2 you must get "development" environment configuration and in pc3 you must get "production" environment configuration. This can be achieved like this:
Create a base configuration file in the config directory, lets say "app.json" and add required configurations to it.
Now simply create folders within the config directory that matches your environment name, in this case "development" and "production".
Next, create the configuration files you wish to override and specify the options for each environment at the environment directories(Notice that you do not have to specify every option that is in the base configuration file, but only the options you wish to override. The environment configuration files will "cascade" over the base files.).
Now create new config instance with following syntax.
var config = require('nodejs-config')(
__dirname, // an absolute path to your applications 'config' directory
{
development: ["pc1", "pc2"],
production: ["pc3"],
}
);
Now you can get any configuration value without worrying about the environment like this:
config.get('app').configurationKey;
This answer is not something new. It's similar to what #andy_t has mentioned. But I use the below pattern for two reasons.
Clean implementation with No external npm dependencies
Merge the default config settings with the environment based settings.
Javascript implementation
const settings = {
_default: {
timeout: 100
baseUrl: "http://some.api/",
},
production: {
baseUrl: "http://some.prod.api/",
},
}
// If you are not using ECMAScript 2018 Standard
// https://stackoverflow.com/a/171256/1251350
module.exports = { ...settings._default, ...settings[process.env.NODE_ENV] }
I usually use typescript in my node project. Below is my actual implementation copy-pasted.
Typescript implementation
const settings: { default: ISettings, production: any } = {
_default: {
timeout: 100,
baseUrl: "",
},
production: {
baseUrl: "",
},
}
export interface ISettings {
baseUrl: string
}
export const config = ({ ...settings._default, ...settings[process.env.NODE_ENV] } as ISettings)

Resources