I've just started using Razzle but it seems like a webpack DefinePlugin is stripping out all the environment variables at runtime and replacing them with pre-baked ones. Since environment variables are the standard way to configure applications in different environments (e.g. heroku, lambda, all 12-factor compatible hosting) I assume there's something I'm missing. Do you have any advice for how to stop razzle replacing everything in process.env?
Many thanks
I second the motion for @alex2 claim, I just found out that it really really replaces the existing process.env because of that DefinePlugin,
I need it NOT be overwritten too
What I do is by breaking static analysis:
let proc = process;
module.exports = proc.exports;
Then require('./env').RUNTIME_ENV
I've personally struggled with this issue and spend several hours figuring out a solution to this issue.
This is inherent to webpack compilation and not related to razzle it self.
After looking into how create-react-app handles this, and porting some javascript and ruby code, across two projects, I've successfully deployed a razzle typescript react app in a docker container on heroku with the following solution:
This script is used as a module to handle runtime env.
export interface EnvironmentStore {
NODE_ENV?: string;
[key: string]: string | undefined;
}
// Capture environment as module variable to allow testing.
let compileTimeEnv: EnvironmentStore;
try {
compileTimeEnv = process.env as EnvironmentStore;
} catch (error) {
compileTimeEnv = {};
// tslint:disable-next-line no-console
console.log(
'`process.env` is not defined. ' +
'Compile-time environment will be empty.'
);
}
// This template tag should be rendered/replaced with the environment in production.
// Padded to 4KB so that the data can be inserted without offsetting character
// indexes of the bundle (avoids breaking source maps).
/* tslint:disable:max-line-length */
const runtimeEnv = '{{RAZZLE_VARS_AS_BASE64_JSON__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________}}';
/* tslint:enable:max-line-length */
// A function returning the runtime environment, so that
// JSON parsing & errors occur at runtime instead of load time.
export const loadRuntimeEnv = (): EnvironmentStore => {
let env;
if (typeof env === 'undefined') {
if (compileTimeEnv.NODE_ENV === 'production') {
try {
env = JSON.parse((Buffer.from(runtimeEnv.trim(), 'base64').toString()));
} catch (error) {
env = {};
const overflowsMessage = runtimeEnv.slice(32, 33) !== null;
// tslint:disable-next-line no-console
console.error(
'Runtime env vars cannot be parsed. Content is `%s`',
runtimeEnv.slice(0, 31) + (overflowsMessage ? '…' : '')
);
}
} else {
env = compileTimeEnv;
}
}
return env;
};
export default loadRuntimeEnv;
usage:
import { loadRuntimeEnv, EnvironmentStore } from './env';
const env: EnvironmentStore = loadRuntimeEnv();
const serverHost: string =env.RAZZLE_SERVER_HOST || 'localhost';
This script is used as the entrypoint point instead of server.js and is used to inject {{RAZZLE_VARS_AS_BASE64_JSON___... }} placehoder with the actual runtime environment variables.
require('newrelic');
const logger = require('heroku-logger');
const path = require('path');
const fs = require('fs');
const PLACEHOLDER = /\{\{RAZZLE_VARS_AS_BASE64_JSON_*?\}\}/;
const MATCHER = /^RAZZLE_/i;
const InjectableEnv = {
inject: function(file, ...args) {
const buffer = fs.readFileSync(file, { encoding: 'utf-8' });
let injectee = buffer.toString();
const matches = injectee.match(PLACEHOLDER);
if (!matches) {
return;
}
const placeholderSize = matches[0].length;
let env = InjectableEnv.create(args);
const envSize = env.length;
const newPadding = placeholderSize - envSize;
if (newPadding < 0) {
console.log('You need to increase your placeholder size');
process.exit();
}
const padding = Array(newPadding).join(' ');
env = InjectableEnv.pad(padding, env);
const injected = injectee.replace(PLACEHOLDER, env);
fs.writeFileSync(file, injected, { encoding: 'utf-8' });
},
create: function() {
const vars = Object.keys(process.env)
.filter(key => MATCHER.test(key))
.reduce((env, key) => {
env[key] = process.env[key];
return env;
}, {});
vars.NODE_ENV = process.env.NODE_ENV;
if (typeof process.env.HOST !== 'undefined' && typeof vars.RAZZLE_SERVER_HOST === 'undefined') {
vars.RAZZLE_SERVER_HOST = process.env.HOST;
}
if (typeof process.env.PORT !== 'undefined' && typeof vars.RAZZLE_SERVER_PORT === 'undefined') {
vars.RAZZLE_SERVER_PORT = process.env.PORT;
}
if (typeof process.env.REDIS_URL !== 'undefined' && typeof vars.RAZZLE_REDIS_URL === 'undefined') {
vars.RAZZLE_REDIS_URL = process.env.REDIS_URL;
}
return Buffer.from(JSON.stringify(vars)).toString('base64');
},
pad: function(pad, str, padLeft) {
if (typeof str === 'undefined')
return pad;
if (padLeft) {
return (pad + str).slice(-pad.length);
} else {
return (str + pad).substring(0, pad.length);
}
}
}
const root = process.cwd();
const serverBundle = path.resolve(path.join(root, '/build/server.js'));
if (fs.existsSync(serverBundle)) {
logger.info('Injecting runtime env');
InjectableEnv.inject(serverBundle);
logger.info('Launching server instance');
require(serverBundle);
}
# You should always specify a full version here to ensure all of your developers
# are running the same version of Node.
FROM node:8.9.4
ENV NODE_ENV=production \
REACT_BUNDLE_PATH=/static/js/vendor.js \
PATH=/app/node_modules/.bin:$PATH \
NPM_CONFIG_LOGLEVEL=warn
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
# use changes to package.json to force Docker not to use the cache
# when we change our application's nodejs dependencies:
COPY package.json yarn.lock /tmp/
RUN cd /tmp \
&& yarn install --production=false --pure-lockfile \
&& mkdir -p /app \
&& cp -a /tmp/node_modules /app \
&& yarn cache clean \
&& rm -rf *.*
# From here we load our application's code in, therefore the previous docker
# "layer" thats been cached will be used if possible
WORKDIR /app
ADD . /app
RUN yarn build
EXPOSE 3000
CMD ["node", "docker-start.js"]
Please note that the overflow message processing is not finished, but I hope this helps
Heroku Buildpack for create-react-app
Inner layer of Heroku Buildpack for create-react-app
Not relevant to the issue but here is the Dockerfile for completeness:
# You should always specify a full version here to ensure all of your developers
# are running the same version of Node.
FROM node:8.9.4
ENV NODE_ENV=production \
REACT_BUNDLE_PATH=/static/js/vendor.js \
PATH=/app/node_modules/.bin:$PATH \
NPM_CONFIG_LOGLEVEL=warn
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
# use changes to package.json to force Docker not to use the cache
# when we change our application's nodejs dependencies:
COPY package.json yarn.lock /tmp/
RUN cd /tmp \
&& yarn install --production=false --pure-lockfile \
&& mkdir -p /app \
&& cp -a /tmp/node_modules /app \
&& yarn cache clean \
&& rm -rf *.*
# From here we load our application's code in, therefore the previous docker
# "layer" thats been cached will be used if possible
WORKDIR /app
ADD . /app
RUN yarn build
EXPOSE 3000
CMD ["node", "docker-start.js"]
@jaredpalmer Can you have a look at this and see whether this solution can be used to solve #356, #394 and this issue in razzle or a child package?
Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal.
ProBot automatically closed this due to inactivity. Holler if this is a mistake, and we'll re-open it.
if anyone want to use .env variables in client side in runtime, use this little package.
https://www.npmjs.com/package/razzle-plugin-runtimeenv
I came across this during a google search for an issue related to this. Full disclosure: im not using razzle but I think this might be helpful.
A way to circumvent webpack's replacement of process.env is to access the env vars in a different way.
You can access it via
const env = process['env'];
console.log(env.RUNTIME_VAR); // should be var passed in at runtime
or by importing process... import * as process from 'process'; basically, any way other than process.env
DefinePlugin only replaces free variables
@albert-the-creator Razzle clear env variable's during development in browser area.
Most helpful comment
I came across this during a google search for an issue related to this. Full disclosure: im not using razzle but I think this might be helpful.
A way to circumvent webpack's replacement of process.env is to access the env vars in a different way.
You can access it via
or by importing process...
import * as process from 'process';basically, any way other thanprocess.envDefinePlugin only replaces free variables