Rails 6 webpacker using environment variables in javascript file - javascript

I want to use environment variables with my Rails 6 webpacker project in my js code follow this document https://webpacker-docs.netlify.app/docs/env
First, I create .env file at the root directory of my project.
Then
config/webpack/environment.js (I can access process.env just as expected from here)
const dotenv = require('dotenv')
const dotenvFiles = [
`.env.${process.env.NODE_ENV}.local`,
'.env.local',
`.env.${process.env.NODE_ENV}`,
'.env'
]
dotenvFiles.forEach((dotenvFile) => {
dotenv.config({ path: dotenvFile, silent: true })
})
environment.plugins.insert(
"Environment",
new webpack.EnvironmentPlugin(process.env)
)
console.log(process.env)
/*
{
.
.
.
RAILS_ENV: 'development',
MY_SECRET: 'This_is_my_secret',
RACK_ENV: 'development',
NODE_ENV: 'development'
}
*/
.
.
.
module.exports = environment
But when I run console.log(process.env) from app/javascript/packs/application.js I get empty object {} instead
I have tried these but have no luck.
Import and run dotenv again in config/webpack/development.js
// config/webpack/development.js
const dotenv = require('dotenv')
dotenv.config()
Change to use dotenv-webpack but the result is not difference.
EDIT
After searching for hours I found a workaround from this Adding plugin to Webpack with Rails
For some reason if you run your rails server(rails s) with bin/webpack-dev-server your variables will be inaccessible.

Related

node dotenv won't work with pm2

I have an application where locally (without pm2) all the environment variables in the .env file work just fine using dotenv.
But on the server where I'm using pm2 to run the app, the environment variables remain undefined.
The pm2 commands I'm using to run the app on server are:
pm2 start myapp/app.js
pm2 startup
pm2 save
dotenv will read .env file located in the current directory.
When you call pm2 start myapp/app.js it won't search for myapp/.env.
.env // It will try to load this, which doesn't exist
myapp/
app.js
So you have two solutions
use path option:
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
Or call your script from inside myapp/
pm2 start app.js
A good pattern here is to remove dotenv from your code and "require" it on the command line. This makes your code nicely transportable between any environment (including cloud-based) - which is one of the main features of environment variables.
Note: you will still need to install dotenv in your project via npm when running it on a server.
a) code up your .env file alongside your script (e.g. app.js)
b) to run your script without pm2:
node -r dotenv/config app.js
c) in pm2.config.js:
module.exports = {
apps : [{
name : 'My Application',
script : 'app.js',
node_args : '-r dotenv/config',
...
}],
}
and then
pm2 start pm2.config.js
note: the use of dotenv/config on the command line is one of the best practices recommended by dotenv themselves
edit 2021: for completeness - as my answer has got some ticks, I wanted to add a 4th option to the list:
d) combined pm2/env config
module.exports = { apps : [{
name : 'My Application',
script : 'app.js',
env : {
PORT: 5010,
DB_STRING: 'mongodb://localhost:27017',
...
},
}]};
This will be useful if you are treating your pm2.config as environmental configuration and outside of git etc. It just negates the need for a separate .env, which may suit you. It negates the need for dotenv completely as pm2 injects the env variables into your script's process
you have kill you pm2 process first
try
pm2 kill
then restart pm2 using
pm2 start app.js
I had the same problem but it wasnt explained clearly so here is the solution based on
github user vmarchaud comment.
This also fixes the issue people had with #Andy Lorenz solution.
In my case i wanted to create an ecosystem file for multiple apps but i was keep getting
Error: Cannot find module 'dotenv/config'
The solution was easy.
You have to declar cwd, aka the project folder where the dotenv/config will be read from.
module.exports = {
apps: [{
name: 'app1 name',
script: 'app1.js',
cwd: '/path/to/folder/',
exec_mode: 'fork_mode',
node_args: '-r dotenv/config',
}, {
name: 'app2 name',
script: 'app2.js',
cwd: '/path/to/folder/',
instances: 'max',
exec_mode: 'cluster',
node_args: '-r dotenv/config',
}],
};
You can parse .env using dotenv lib end set them manually in ecosystem.config.js
ecosystem.config.js:
const { calcPath, getEnvVariables } = require('./helpers');
module.exports = {
apps: [
{
script: calcPath('../dist/app.js'),
name: 'dev',
env: getEnvVariables(),
},
],
};
helpers.js:
const path = require('path');
const dotenv = require('dotenv');
const fs = require('fs');
function calcPath(relativePath) {
return path.join(__dirname, relativePath);
}
// this function will parce `.env` file but not set them to `process.env`
const getEnvVariables = () => {
const envConfig = dotenv.parse(fs.readFileSync(calcPath('.env')));
const requiredEnvVariables = ['MODE'];
for (envVariable of requiredEnvVariables) {
if (!envConfig[envVariable]) {
throw new Error(`Environment variable "${envVariable}" is not set`);
}
}
return envConfig;
};
None of this worked for me because I was using cluster mode.
I installed dotenv as dev dependency at the root (I was using yarn workspaces too).
Then I did this:
require('dotenv').config({ path: 'path/to/your/.env' })
module.exports = {
apps: [
{
name: 'app',
script: 'server/dist/index.js',
instances: 2,
exec_mode: 'cluster',
instance_var: 'APP_INSTANCE_SEQ',
// listen_timeout: 10000,
// restart_delay: 10000,
}
]
}
I use a much simpler version of #Marcos answer:
.env
app.js
for example we need to store token in .env file and pass it right to app.js:
inside .env
token=value
inside app.js:
require('dotenv').config();
console.log(process.env.token)
Also, don't forget. If you add .env file to .gitignore and then git pull you repo on VPS or smth, you need to copy .env file manually, otherwise your app won't work.
And in some cases it's important in what area you are using your config, so make sure that NODE_ENV=production string is added to your .env file.
After all you could use pm2 start app.js right from your app's folder.
This was my project setup..
/src/app.ts
which than compiled into dist folder.
/dist/app.js
my .env file was outside dist folder so it wasn't accessible.
this is the command i tried.
pm2 start app.js --env=.env

Setting environment variables in Gatsby

I used this tutorial: https://github.com/gatsbyjs/gatsby/blob/master/docs/docs/environment-variables.md
Steps I followed:
1) install dotenv#4.0.0
2) Create two files in root folder: ".env.development" and ".env.production"
3) "follow their setup instructions" (example on dotenv npm docs)
In gatsby-config.js:
const fs = require('fs');
const dotenv = require('dotenv');
const envConfig =
dotenv.parse(fs.readFileSync(`.env.${process.env.NODE_ENV}`));
for (var k in envConfig) {
process.env[k] = envConfig[k];
}
Unfortunately, when i run gatsby develop, NODE_ENV isn't set yet:
error Could not load gatsby-config
Error: ENOENT: no such file or directory, open 'E:\Front-End Projects\Gatsby\sebhewelt.com\.env.undefined'
It works when I set it manually:
dotenv.parse(fs.readFileSync(`.env.development`));
I need environment variables in gatsby-config because I put sensitive data in this file:
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: envConfig.CONTENTFUL_SPACE_ID,
accessToken: envConfig.CONTENTFUL_ACCESS_TOKEN
}
}
How to make it work?
PS: Additional question - As this made me think, I know I shouldn't put passwords and tokens on github, but as netlify builds from github, is there other safe way?
I had a similar issue, I created 2 files in the root ".env.development" and ".env.production" but was still not able to access the env file variables - it was returning undefined in my gatsby-config.js file.
Got it working by npm installing dotenv and doing this:
1) When running gatsby develop process.env.NODE_ENV was returning undefined, but when running gatsby build it was returning 'production' so I define it here:
let env = process.env.NODE_ENV || 'development';
2) Then I used dotenv but specify the filepath based on the process.env.NODE_ENV
require('dotenv').config({path: `./.env.${env}`});
3) Then you can access your variables for your config:
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: `${process.env.CONTENTFUL_ID}`,
accessToken: `${process.env.CONTENTFUL_TOKEN}`,
},
},
],
}
You should only use env files when you're comfortable checking those into git. For passwords/tokens/etc. add them to Netlify or whatever build tool you use through their dashboard.
These you can access in gatsby-config.js & gatsby-node.js via process.env.ENV_VARIABLE.
You can't access environment variables added this way in the browser however. For this you'll need to use .env.development & .env.production.
I really dislike the .env.production file pattern, our build system sets up and uses env variables and having extra build steps to write those into a file is weird. But Gatsby only whitelists GATSBY_ of the env vars, with no obvious way of adding your own.
But doing that isn't so hard, you can do it by adding something like this in the gatsby-node.js file:
exports.onCreateWebpackConfig = ({ actions, getConfig }) => {
const config = getConfig();
// Allow process.env.MY_WHITELIST_PREFIX_* environment variables
const definePlugin = config.plugins.find(p => p.definitions);
for (const [k, v] of Object.entries(process.env)) {
if (k.startsWith("MY_WHITELIST_PREFIX_")) {
definePlugin.definitions[`process.env.${k}`] = JSON.stringify(v);
}
}
actions.replaceWebpackConfig(config);
};
After doing a few searches, I found that we can set environment variables through netlify website, here are the steps:
Under your own netlify console platform, please go to settings
Choose build & deploy tab (can be found on sidebar)
Choose environment sub-tab option
Click edit variables and add/put your credentials in
Done!

Vue & Webpack: Global development and production variables

I'm using vue-cli to build my web app. My app uses an api in a number of places like so:
axios.post(API + '/sign-up', data).then(res => {
// do stuff
});
The API variable is a constant containing the beginning of the address, e.g., https://example.com.
How would I detect whether this is a dev or prod build and set that variable accordingly? For now, I have a script tag in my index.html document and I am manually changing it for dev and prod:
<script>var API = 'https://example.com'</script>
Is there a better way to handle this?
If you're using the vue-cli webpack template, within the config folder you'll see two files: dev.env.js and prod.env.js.
Both files contain an object that is globally available throughout your Vue app:
dev.env.js
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
prod.env.js
module.exports = {
NODE_ENV: '"production"'
}
Note that string values require the nested single and double quotes. You can add your own variables like so:
dev.env.js
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
API: '"http://localhost:8000"'
})
prod.env.js
module.exports = {
NODE_ENV: '"production"',
API: '"https://api.example.com"'
}
Now the variable can be accessed within any Vue method from the process.env object:
process.env.API

Dotenv not loading properly

I am trying to access some environment variables using process.env that were loaded by dotenv.
My folder structure :
.env
src
-- - server.js
My server.js configuration :
(...)
import auth from './middleware/auth'
import dotenv from 'dotenv'
dotenv.load({
path: '../',
silent: process.env.NODE_ENV === 'production'
})
auth()
// Instantiate app
const app = express();
The file where I try to access process.env variable :
(...)
module.exports = function() {
console.log("env", process.env.MONGODB_URI)
var options = {};
options.jwtFromRequest = ExtractJwt.fromAuthHeader()
options.secretOrKey = process.env.JWT_SECRET
Which logs env, undefined, and then crashes with
TypeError: JwtStrategy requires a secret or key
Even if I move .env into src (same directory as server) and remove path in config, it fails.
It appears that when you specify the path, you need to make it full:
require('dotenv').config({path: __dirname + '/../.env'});
.env being your file
Try this; this should work.
import {} from 'dotenv/config'
import somethingElse from 'somethingElse'
...
[the rest of your code]
This works because of how ES6 modules imports modules.
If you want to dig into more.
Please refer this. https://hacks.mozilla.org/2015/08/es6-in-depth-modules/
As a summary :
When you run a module containing an import declaration, the modules it
imports are loaded first, then each module body is executed in a
depth-first traversal of the dependency graph, avoiding cycles by
skipping anything already executed.
Hope this will help someone.
I'm using require('dotenv').config() on my main nodejs .js entry file and it works just fine.
From the docs:
Path
Default: .env
You can specify a custom path if your file containing environment
variables is named or located differently.
require('dotenv').config({path: '/custom/path/to/your/env/vars'})
use may use:
require('dotenv').config({ path: require('find-config')('.env') })
This will recurse parent directories until it finds a .env file to use.
You can also alternatively use this module called ckey inspired from one-liner above.
.env file from main directory.
# dotenv sample content
USER=sample#gmail.com
PASSWORD=iampassword123
API_KEY=1234567890
some js file from sub-directory
const ck = require('ckey');
const userName = ck.USER; // sample#gmail.com
const password = ck.PASSWORD; // iampassword123
const apiKey = ck.API_KEY; // 1234567890
If you're using a mono-repo which uses a single .env file across multiple packages/work-spaces you can use the following to find the root .env file.
Install the find-up package from npm: https://www.npmjs.com/package/find-up
import find from 'find-up';
export const findEnv = () => find.sync(process.env.ENV_FILE || '.env');
you have to set the dotenv configs at the very top level of your app:
import dotenv from 'dotenv'
dotenv.load({
path: '../',
silent: process.env.NODE_ENV === 'production'
})
(...)
import auth from './middleware/auth'
auth()
// Instantiate app
const app = express();
The order of imports in this case matters since you are loading the environment variables.

How do I get Winston to work with Webpack?

I have an electron application which is using node.js. I would like to use Winston for logging in the application. I've added winston to my package.json file, but when I run the build command for webpack I'm getting some warnings from the colors.js dependency in winston.
'...the request of a dependency is an expression...'
It then references winston and colors.js. Ignoring the warnings doesn't work, as the electron application gets an exception trying to load some files from winston.
I did some digging on SO and the github site and they say that colors.js has some dynamic require statements that webpack is having issues with. I've also seen that other sample projects seem to have winston up and running without any issues in their projects. Does anyone know how to correctly include the winston logging package with webpack in an electron app?
There are two sides to this issue:
1) winston directly or indirectly depends on color.js, so that dependency automatically gets included, once winston is there. In some older versions of it, it included a dynamic require statement, which leads to this:
2) a dependency has a dynamic require statement that Webpack cannot handle; you can either configure webpack so it can ignore this specific case, or also upgrade winston to a newer version, so color.js will be picked in a variant without that dynamic require (see https://github.com/winstonjs/winston/issues/984).
To tell Webpack to get along with the dynamic require, you need to tell Webpack that Winston is an external library.
Here's an example from my webpack.config.js:
externals: {
'electron': 'require("electron")',
'net': 'require("net")',
'remote': 'require("remote")',
'shell': 'require("shell")',
'app': 'require("app")',
'ipc': 'require("ipc")',
'fs': 'require("fs")',
'buffer': 'require("buffer")',
'winston': 'require("winston")',
'system': '{}',
'file': '{}'
},
To make the logger available in an angular 2 app using electron, create a logger.js file and then wrap it with a global logging service TypeScript file (i.e. logging.service.ts). The logger.js file creates the logger variable with the desired Winston configuration settings.
logger.js:
var winston = require( 'winston' ),
fs = require( 'fs' ),
logDir = 'log', // Or read from a configuration
env = process.env.NODE_ENV || 'development',
logger;
​
winston.setLevels( winston.config.npm.levels );
winston.addColors( winston.config.npm.colors );
if ( !fs.existsSync( logDir ) ) {
// Create the directory if it does not exist
fs.mkdirSync( logDir );
}
logger = new( winston.Logger )( {
transports: [
new winston.transports.Console( {
level: 'warn', // Only write logs of warn level or higher
colorize: true
} ),
new winston.transports.File( {
level: env === 'development' ? 'debug' : 'info',
filename: logDir + '/logs.log',
maxsize: 1024 * 1024 * 10 // 10MB
} )
],
exceptionHandlers: [
new winston.transports.File( {
filename: 'log/exceptions.log'
} )
]
} );
​
module.exports = logger;
logging.service.ts:
export var LoggerService = require('./logger.js');
Now the logging service is available for use throughout the application.
Example:
import {LoggerService} from '<path>';
...
LoggerService.log('info', 'Login successful for user ' + this.user.email);

Categories

Resources