I have a different URL for our api depending if it's development or production for a react app.
Using webpack, how can I set an env var like __API_URL__ and then change that depending if it's built using webpack.config.dev vs webpack.config.prod
I thought the answer may be in webpack.DefinePlugin but no luck.
new webpack.DefinePlugin({
__API_URL__: 'localhost:3005',
}),
I'd expect __API_URL__ to be available as a global but no such luck.
What would be the right way to do this? Also key thing is that no express server on the prod deploy. So this has to happen during the build...
As Michael Rasoahaingo said, the DefinePlugin works similar like replacing values with regular expressions: It replaces the value literally in your source code. I would not recommend to use the DefinePlugin for this kind of task.
If you want to switch configs based on the environment, you could use resolve.alias for that. Just import your config like this:
var config = require("config");
and then add a mapping in your webpack.config.js:
resolve: {
alias: {
config$: require.resolve("path/to/real/config/file")
}
}
DefinePlugin is not working as you expected. It doesn't expose __API_URL__ as a global variable.
According to the documentation: "The values will be inlined into the code which allows a minification pass to remove the redundant conditional."
So, it will find all occurence of __API_URL__ and changes it.
var apiUrl = __API_URL__
and
__API_URL__: '"localhost:3005"' // note the ' and "
become
var apiUrl = "localhost:3005"
Related
When I make a change to my react-app, and it tries to reload, I get the following error:
/path/to/app/node_modules/webpack/lib/node/NodeWatchFileSystem.js:50
changes = changes.concat(removals);
^
TypeError: changes.concat is not a function
I then have to restart my react app in order for the changes to be reflected.
This started when we did a bunch of fiddling with versions of different packages, so I suspect that there's some combination of packages that webpack is not happy with, but I don't know which ones, or how to figure that out.
Just in case it's relevant, though I don't think it is, here's the webpack section of my craco.config.js:
webpack: {
plugins: [
new DefinePlugin({
BRAND_NAME: '"My app"'
})
],
configure: webpackConfig => {
const instanceOfMiniCssExtractPlugin = webpackConfig.plugins.find(
plugin => plugin instanceof MiniCssExtractPlugin
);
if (instanceOfMiniCssExtractPlugin)
instanceOfMiniCssExtractPlugin.options.ignoreOrder = true;
return webpackConfig;
}
}
Package.json link
Webpack and Watchpack Versions
The error seems to be in reference to v4 of webpack (or earlier). This function was reworked in v5 of webpack, where it now no longer assumes changes is an array (it's now an iterable that is only iterated if inputFileSystem.purge is defined).
From the resolution section of package.json, it looks like you're resolving watchpack to version 2.2.0. From the v2 release notes, it looks like it changed its API to now pass sets instead of arrays to watcher.once("aggregated", (changes, removals) => {...}), breaking v4 of webpack.
Updating to version 5 of webpack may solve this particular issue (though you may need to upgrade other packages that depend on webpack v4, like webpack-cli). Alternatively, downgrading watchpack to before version 2 (like version 1.7.5) might also work, though this may also introduce new issues.
Please check your array before performing the concat operation. You can also use the Array.from() method before using concat. For example,
const arr1 = ['a', 'b'];
const arr2=['d', 'c'];
const arr3 = Array.isArray(arr1) ? arr1.concat(arr2) : []; //check if arr1 is a array
const arr4=Array.from(arr1).concat(arr2); //use type conversion.
Try the above methods in the node modules folder of where the error is from.
So I'm trying to understand how to use .env to secure my api key & data for my login auth. I've followed the process of the dotenv package and the stackoverflow answer here.
What I did:
1. I installed the dotenv package
2. In my rootfolder, I added a .env with only one line:
API_AUTH=http://myapilink.com
3. In my App.js, I added following line:
require('dotenv').config()
console.log(process.env.API_AUTH)
However, console.log returns 'undefined', why is that? What have I missed?
You have to prefix your environment variables with REACT_APP_. See here.
Note: You must create custom environment variables beginning with
REACT_APP_. Any other variables except NODE_ENV will be ignored to
avoid accidentally exposing a private key on the machine that could
have the same name. Changing any environment variables will require
you to restart the development server if it is running.
So instead of just API_AUTH it would be REACT_APP_API_AUTH instead.
And it will be exposed to your app as process.env.REACT_APP_API_AUTH.
Dotenv only works on server side. It needs an environment to store the variables. Here, we extract the content of the .env file and reduce it to an Object and pass it to Webpack's DefinePlugin allowing us later usage (process.env.API_AUTH):
const webpack = require('webpack');
const dotenv = require('dotenv');
module.exports = () => {
const env = dotenv.config().parsed,
envKeys = Object.keys(env).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(env[next]);
return prev;
}, {});
return {
plugins: [
new webpack.DefinePlugin(envKeys)
]
};
I'm currently a bit confused about react and env variables. Basically, what I would like to achieve is, to have different files. Something like: enviorment.dev.js, enviorment.prod.js.
I couldn't find the documentation and there seem to be a lot of different options to choose from.
I guess, I look for something like this: https://medium.com/beautiful-angular/angular-2-and-environment-variables-59c57ba643be just for react.
If you're using webpack, there's a plugin, but that's the sort of place where you would need to configure that. As Felix said, React doesn't do that:
https://webpack.js.org/plugins/define-plugin/
webpack.config:
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify("5fa3b9"),
BROWSER_SUPPORTS_HTML5: true,
TWO: "1+1",
"typeof window": JSON.stringify("object")
})
index.js:
if (!PRODUCTION) {
console.log('Debug info')
}
if (PRODUCTION) {
console.log('Production log')
}
I am looking to parameterize the baseURL to run in several different environments, like "local," "dev" "test" "prod" etc. I can think of two ways of doing this.
Pass the baseURL param at run time.
Create a separate GULP task for each baseURL.
I think I want to go with option #2 but wanted to check with some others on this.
Thanks,
Tyler
There is not much of a difference between 1. and 2., as you have to pass baseUrl param in a gulp task?
Both of them require change of code in case of new environment (that means commit-push - pull-request - find someone for code review... at least for me :) )
I think it is better to separate baseUrl from code completely into env variable:
onPrepare: function() {
//load env variables for testing
if (typeof process.env.BASE_URL !== "undefined") {
browser.baseUrl = process.env.BASE_URL;
console.log('Base URL = ' + browser.baseUrl);
}
I am developing a frontend using the Backbone.js and require.js and everything is going well till i need to create a file named it config.js to store some defaule values to use it in the whole of the application
below is the code of the config.js file
// Filename: config.js
define([''], function(){
var baseUrl = "http://localhost:8888/client/",
apiServer = "http://api-server:8888";
return function(type){
return eval(type);
};
});
in one of my views I would define the config.js then i can access the value of both
var baseUrl = "http://localhost:8888/client/",
apiServer = "http://api-server:8888";
via this line of code below that i put it inside any *.js file on my application
var baseUrl = config('baseUrl');
console.log(baseUrl); //prints out this > http://localhost:8888/client/
the problem here is i am using eval to get the value of what kind of value i need to retrieves, I know it's not safe method to use but could anyone suggest safe solution
RequireJS lets you define objects just like you define more complicated modules. You can have a config module and then use it in whichever other files that require it.
Inside config.js you can do:
define({
baseUrl:"http://localhost:8888/client/",
apiServer:"http://api-server:8888"
});
Then require it in other modules:
//someotherfile.js , defining a module
define(["config"],function(config){
config.baseUrl;// will return the correct value here
//whatever
});
Side note: You can use actual global state (defining the variable on window) but I strongly urge you not to since this will make testing hard, and will make the dependency implicit and not explicit. Explicit dependencies should always be preferred. In the above code and unlike the global it's perfectly clear that the configuration is required by the modules using it.
Note, if you want values that are not valid identifiers you can use bracket syntax too config["baseUrl"] the two (that and config.baseUrl) are identical in JavaScript.
As an alternative solution (and uglier than Benjamin's) you can put both urls into an object:
define([''], function(){
var urls = {
baseUrl: "http://localhost:8888/client/",
apiServer: "http://api-server:8888"
};
return function(type){
return urls[type];
};
});
Still, simply exporting an object is much cleaner.