Gatsby, Environment variables not accessible in browser - reactjs

I want to use environment variables. I created .env.development file and I put some variables. Then I include the dotenv plugin to read the variables in gatsby-config.js:
require('dotenv').config({
path: `.env.${process.env.NODE_ENV}`
});
The content of my .env.development:
GATSBY_APP=MYAPP
It's working in gatbsy-node.js but in browser (REACT) it's empty. I display console.log(process.env) and it return empty object.
Even if I install and configure gatsby-plugin-env-variables.

It looks like you're combining two approaches, and that might be where you're running into trouble.
Gatsby comes out-of-the-box with support for defining environment variables in environment-specific .env.[environment] files (e.g. .env.development), provided these files are in the root of your project (i.e. your-project/.env.development). Documentation for this feature. You do not need to install or configure dotenv for this to work.
Another approach is to use dotenv, which will allows you to use a general .env file. You need to then import and configure the tool, which is generally done at the very top line of gatsby-config.js and looks like this:
require("dotenv").config()
Note that you do not specify the environment name (e.g. development) in this scenario, and you would not commit the .env file to your repository.
The other issue you might run into is that part of your code runs server-side, using Node, and part runs client-side (in the browser). Since process.env is only available in Node, Gatsby does some additional work to make it available in the browser. We don't want all of these variables, which frequently hold secrets, to be provided to the browser, though, so Gatsby only copies over those that whose names begin with GATSBY_. Finally, as a side effect of the way that these variables get copied over, you must explicitly reference them for your build to work:
// this is okay everywhere
const GATSBY_APP = process.env.GATSBY_APP
// this won't work in code that runs client-side, but will work
// in `gatsby-node.js` and other files that only run in Node
const { GATSBY_APP } = process.env

If you wanted to whitelist your own environment variables, either as a prefix (shown here) or have a list of them, you can add 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);
};

I was running into the same issue and just found out what I was doing wrong. It looks like you're doing the same thing.
The explanation both the documentation and #coreyward give are correct, however, note that while corey implies they're two different approaches and the issue might be that they're conflicting, they're not, they do different things - so that code of yours is fine.
What's wrong, though, is that you're console logging process.env. And as it turns out, this will always output an empty object even though the variables might be there.
You have to console.log(process.env.GATSBY_APP) directly to see any value.
(Reference)
This is essentially an extension of what Corey pointed out in his "finally as a side-effect" section, however, the way he's written it implies it's an issue with destructuring - but it's not limited to that.
In my case, I was doing the above error AND not exposing the variables with GATSBY_ properly, then while testing I started logging the whole env object like you while I searched for the cause. This meant that even after I added the correct GATSBY_ prefix I still couldn't see anything.
So in short, it's probably only your console.log line that's an issue - access the variable directly, not the env object as a whole.

The Gatsby site as a well documented section on working with environment variables. The gist is that the environment variables are only available during build time when the internal nodejs server is server rendering your site. In order, get those environment variables in the browser you need to programmatically embed them using the special gatsby-*.js files. The example they provide seems to be close to what you want to achieve.

Related

How to get environment variables in the aAPI side from the UI?

React noob here. I'm currently migrating an app from Next.js to React.
In Next, we had certain environment variables (such as root URLs) defined in our Jenkinsfile that would be called from our UI via process.env.[var].
Now that we're migrating to React, I can't use process.env in the UI side, as this is a Node property. I also can't store these environment variables on the API side and call them from the UI side, as React doesn't allow references to be made outside the UI's /src directory.
Is there a way to use our environment variables (defined in Jenkinsfile) from our UI? I'm considering creating a GET endpoint in the API side to return the environment variables to the UI when it loads, but would like to know if there are any alternative solutions.
Probably obvious, but here's the basic layout of our app
-api
-src
-ui/react
-src
-utils
constants.js <- trying to store the env vars here
jenkinsfile
Technically, we can't use environment variables in browser context, that's why we usually use DefinePlugin or EnvironmentPlugin in webpack based projects like CRA and Vue-CLI to statically replace process.env.* with environment variables.
But this way will forces us to rebuild the whole application multiple times for different stages.
To fix this, without compromises., I recommend trying import-meta-env.
During production, this plugin statically replaces import.meta.env.* with placeholders (we use import.meta because process.env is a Node specific object), then we can run the built-in script and replace the placeholders with real environment variables later.
I also created an example for Docker. For Jenkins, I think it can be done in the same way.
Hope this helps someone who needs it.
If you are using create-react-app you can define your env variables in the .env file, just prefix them with REACT_APP_.
If not, you can use build tools like webpack to add them for you

Why lodash get returns undefined for nested object in the react production build?

Actually, I know my question is s weird but really confused me. lodash functions are awesome and using them is a safe way to have a secure and clean code but in my react project when I use get function of lodash, in the development environment it works well as every developer expected.
import get from 'lodash/get';
const hostname = get(window, 'location.hostname');
// => returns the current hostname
But after I run yarn build && yarn start to make a production build of my ReactJS app and run it, its behavior is changed and returns undefined.
import get from 'lodash/get';
const hostname = get(window, 'location.hostname');
// => returns undefined
So in the production build, I try the get(window, 'location') and it returns the location object. but when I put a key of location the lodash get function returns undefined.
Actually, in the production and just for nested it returns undefined
Due to this issue, I just found this post, but no one answer it.
According to this comment I became passionate about testing my production build. So I listed all thing That happens on the production build of my project that maybe causes this issue.
At first, I doubted to babel plugins that I used for transpile, but none of them affect just on production, they affect for whole environments.
And at last, I find LodashModuleReplacementPlugin. this plugin causes this issue.
Based on its doc:
Deep property path support for methods like _.get, _.has, & _.set.
This plugin omit the deep path support on lodash get. That's the point.

.env shows incorrect values

So I have a file named .env with the following contents
NODE_PATH=./src
NODE_ENV=what
TEST=test
And I am calling that in my index.js in my react app.
require("dotenv").config();
console.log(process.env);
...
shows the following output
NODE_ENV: "development"
PUBLIC_URL: ""
I thought maybe I declared another .env file somewhere else, but thats not the case. I searched my project for the PUBLIC_URL and it's not located anywhere in my project. I don't even know what else to check at this point.
In react code you have to compile the environment variables in at, well, compile time because at run-time there it is only possible to access a fake process.env object. Unless you are using server side rendering.
See also: Passing environment-dependent variables in webpack
If you're using CRA then you'll need to do: REACT_APP_TEST=test and reload the dev server to have it show up as expected in your app.
If you have used create-react-app for bootstrap your project then you have to use environment variables like REACT_APP_NODE_ENV=development.
After adding any new environment variable, you have to restart the development server.

Environment configuration at build time with npm

There are several blog posts that explain why switching from grunt or gulp to building with just plain npm is a good idea for example this one by Cory Hourse or this one by Keith Cirkle. One thing these blog posts do not explain is how I can easily do environment configuration. For example a common requirement is to have different REST API locations. During development the server might be running on localhost:8080, but on production it should be accessed through a relative URL such as /api or /rest/api and the port or protocol are different for development and production.
There are several solutions for this. For example grunt supports template strings like <% %> and <%= %> and there are grunt or gulp plugins like in this question about grunt-ng-config. These solutions are specific to Angular (which I am using), but I am not necessary looking for an AngularJS specific solution.
I also know of the angular-environment plugin, but as far as I can see this does configuration at run time and I am looking for something that can do this at build time.
So what I am looking for is something that allows me to configure my application at build time either by replacing some template strings or by generating a Javascript file with some variables that I can read at run time.
One requirement is that it should be OS independent. So I do not want to use UNIX specific tools such as sed to rewrite a file. And due to different variable expansion (e.g. % vs. $) a solution should not rely on environment variables.
Is there an existing solution or a best-practice for this?
due to different variable expansion (e.g. % vs. $) a solution should
not rely on environment variables
this cuts off your best solution. Why not rely on env vars? node provides
process.env
to access env vars. You could create custom gulp / grunt tasks that use process.env instead of the "different variable expansions" you refer to.
You can use, for example, Jade templating to pass env var values to your HTML at build time. This would generate your index.html on the fly as part of the build process and add relevant classes based on env vars.
For example, according to the value of an env var you might set a class on the HTML tag.
This might reflect the customer.
Then you could have some CSS
.customer1 .myimage {
background-image: url("customer1.png");
}
.customer2 .myimage {
background-image: url("customer2.png");
}
or you could use JavaScript to detect which class was added to head during the build.

gruntjs / angularjs - optional development config?

Like most js web apps we have a config.js file that contains global config information about the app, base api urls and such. These values are often different in local development than in production.
I've looked at answers like: Development mode for AngularJS using GruntJS, and also things like grunt-replace for creating an on-the-fly config file.
My issue is that the "development" part varies from developer to developer, we all need a version of the API setup so the base api urls will be different. I'd like to allow each developer to override specific variables in the config in a way that doesn't require them to commit that info to the git repo (I agree that this isn't best practice, everything should be in the repo, but as this is only 1/2 variables for this project I can overlook it)
Any ideas on how to achieve this setup?
You can use grunt-preprocess. I would have production (and dev-server, etc) values in a file, say env.json. You could use grunt to look for an optional file, say overrides.json or developer.json, which would extend/overwrite the values from env.json.
var envFile = require('./env.json');
You can create command line options to grunt with grunt.option, e.g. var env = grunt.option('env') || undefined;, which could be used to turn off overriding.
You can get data from the optional file using fs.existsSync:
var fs = require('fs');
var developerFile;
if (fs.existsSync('./developer.json')) {
developerFile = require('./developer.json');
}
The simplest way to define the grunt-preprocess context would be to use the developer.json file if present, or the env.json file if not:
context: developerFile ? developerFile : envFile;
This requires the developer file to be complete. An alternative is to extend the envFile with options from developerFile if it's present.
In my project, we use different config files (which are basically files with JS object). So every developer has his app/configs/developer/config.js file, which is not comited in the source control, so every developer has his own setup. Project uses link to app/scripts/config.js by default and this file is just a soft link to developers config file. However, there are also app/configs/staging/config.js and app/configs/production/config.js files, which are replaced when using gruntjj to build project. Those configs are just copied to build solution instead of soft linked file.
I hope that makes sense..

Resources