facebook / create-react-app

Set up a modern web app by running one command.
https://create-react-app.dev
MIT License
102.77k stars 26.87k forks source link

A way to define custom environment variables #102

Closed meghprkh closed 8 years ago

meghprkh commented 8 years ago

This may be a tiny yet non-obtrusive solution. Allow a custom .env.js file that exports a key value pair used for custom webpack defines. I guess this wont be useful for many, since you any way get to refer to NODE_ENV but I also used this for defining a publicPath from an environment variable or defaulting to root. (useful for me as i had to run the same app for multiple quizzes, so I could build the frontend without configuring anything and having sane default not-get-in-the-way configs while development and testing)

gaearon commented 8 years ago

Can you please clarify your use case in a little more detail? I'm specifically interested in the problem you were solving (configuring for multiple quizzes?) than the exact proposed solution.

dmi3y commented 8 years ago

I might have one. Let's say there is api server running at different enviroments like so:

And I'm building SPA which supposed to use CORS and being hosted anywhere, just like static site.

And such I want to supply dev/prod absolute paths for different versions of SPA.

Hope this makes sense.

mxstbr commented 8 years ago

I think at that point you have a very advanced use case and ejecting is the way to go. I don't think adding .env support by default is really necessary, since most users will never need it?

meghprkh commented 8 years ago

@mxstbr Yes may be true, but I just wanted to open this for some discussion, please feel free to close it.

I try to explain my use which is somewhat similar to @dmi3y's usage:

Basically the .env.js file can act as a way to preprocess any constants without having to include any file in the main app. So for example if we are going to use homepage of package.json, we can include package.json in the .env file and export the constants after a little modification also. However if we do this in the main app, it is not preprocessed and package.json will be included in the app bundle also. While not an issue here because it would be anyways minifed, it will be an issue when we have to use a node module for generating a constant and thus bundle size may increase or it may not work

This is an advanced use case but not a obtrusive solution (not needed but can be provided kind of thing).

I hope this explains clearly the purpose.

mxstbr commented 8 years ago

Yes may be true, but I just wanted to open this for some discussion, please feel free to close it.

Absolutely no fault in that, we're definitely not saying create-react-app is perfect by far! Discussions like these could open our eyes to use cases we haven't though of, they're incredibly important.

I'm not sure I fully understand your use case, wouldn't you need access to the webpack config anyway if you're changing the publicPath?

meghprkh commented 8 years ago

@mxstbr not as per https://github.com/facebookincubator/create-react-app/issues/21 where it would be automatically understood from package.json's homepage

d-gubert commented 8 years ago

Something I missed when trying create-react-app in a cloud9 free workspace was the ability to set a custom IP and PORT for the server to listen to.

I was able to achieve that by manually changing thestart script in react-scripts, but I think using environment variables for that would be much simpler, even more for begginers.

tyrbo commented 8 years ago

^ That's also my use case. I need to adjust the ip/port for usage on Cloud9.

vjeux commented 8 years ago

I'm curious, can you explain how the workflow works with cloud9? Do you have the developer server running in the cloud and use their browser text editor to edit the code? Where do you see the terminal output? Why not develop locally?

d-gubert commented 8 years ago

I mainly use this kind of service (Cloud9, Nitrous.io, etc...) when I'm at work and don't have access to my PC.

They're also a good place to study with friends, since they allow collaborative coding. Plus, somewhere free to showcase a small app?

And yes, you use their browser text editor to edit the code and there's also a terminal emulator in the text editor.

lacker commented 8 years ago

One use case I often have for environment variables is when I want to run some code in production but not in development. For example, if you're using an analytics service, you might not want to log any analytics from development versions of the app. Or if you use an external service to sign people up for a mailing list, you don't want your test accounts getting in there.

About Cloud9, one situation I see it adopted more and more is for education. If you're teaching a class of people how to use some technology, you don't want to spend a ton of time making sure everyone has their development environment set up. If you can just give them one Cloud9 config they can get going quickly. So it would be pretty nice to support Cloud9. They also just got acquired by Amazon to be integrated into AWS, which is another sign this sort of thing is going to grow in popularity.

gaearon commented 8 years ago

One use case I often have for environment variables is when I want to run some code in production but not in development.

We currently already allow this with process.env.NODE_ENV checks. This is how React determines which code path to use.

So it would be pretty nice to support Cloud9

Absolutely agree. I just want us to learn more about different use cases first, and approach the problem holistically, rather than add flags one by one over time. A holistic solution might turn out to be simpler.

lacker commented 8 years ago

One use case I often have for environment variables is when I want to run some code in production but not in development.

We currently already allow this with process.env.NODE_ENV checks. This is how React determines which code path to use.

Hmm good point. Here's a slightly different example - let's say your app accesses a Firebase backend. There is one that's only used for production, but each developer has their own for local development, so you can stick random experimental stuff in your database without it borking other peoples' environments. One pattern I would use for this is to check an environment variable to get access info for the backend, and have a startup script for development mode that grabbed this from a config file that isn't checked into source control. Is there currently a straightforward way to do this within a create-react-app-created app?

mxstbr commented 8 years ago

Maybe I'm missing something here, but what about defining two different scripts in your package.json?

{
  "dev": "SOME_ENV_VAR='development' npm start",
  "prod": "SOME_ENV_VAR='production' npm start"
}

and then I think in your app you could have a constants.js file that exports some commons env vars like that URL:

if (process.env.SOME_ENV_VAR === 'development') {
  // export different stuff here
} else {

}

Not 100% sure how a .env file would make that easier?

d-gubert commented 8 years ago

The problem I encountered is that IP and PORT are hard coded on the start script, which isn't exactly in the app code.

So just defining some environment variables before running the server is not enough.

gaearon commented 8 years ago

Maybe I'm missing something here, but what about defining two different scripts

This currently won’t work because we don’t pass all ENV variables to DefinePlugin. We only pass NODE_ENV. We could, however, pass all variables! (This doesn’t seem super problematic to me, and maybe it’s even the right thing to do.)

gaearon commented 8 years ago

The problem I encountered is that IP and PORT are hard coded on the start script, which isn't exactly in the app code.

We definitely wouldn’t be hardcoding them when we get to solving this problem. I’m just saying I want to approach this holistically and not just add a bunch of variables one by one without any cohesive system.

Let’s figure out a generic enough system that lets us configure create-react-app for many scenarios: custom port, serving from cloud9, customizing environment, etc.

pke commented 8 years ago

Sometimes, like with Code-Push, you have additional constellations like this:

App Code-Push Deployment
DEV STAGING
PROD STAGING
PROD PROD

So you would need another ENV variable like CODEPUSH_ENV. So yes, @gaearon I agree we should just throw all the env strings into DefinePlugin. In fact I'd go so far and say that should be the default behaviour of DefinePlugin ;)

vjeux commented 8 years ago

Does DefinePlugin dumps all the environment variables in a js object in the generated output or does a find/replace of all of them?

There are a lot of very sensitive information in environment variables from a security perspective, this is a big attack vector and we don't want to dump it all by default.

ForbesLindesay commented 8 years ago

It does a find and replace. Not only is that important for security, it also ensures that dead-code-elimination works properly after the environment variables are set.

On the other hand, I would probably not want any npm module I install to be able to expose any of my environment variables, that seems like an auditing nightmare. My inclination would be to do NODE_ENV for all modules (including npm) but maybe do custom environment variables only in application code.

gaearon commented 8 years ago

We could choose a namespace, like REACT_APP_ENV.

lacker commented 8 years ago

Maybe this could be solved instead with dynamic imports - if you do something like

let env;
if (process.env.NODE_ENV == "production") {
  env = require("production-env.js");
} else {
  env = require("development-env.js");
}

then you could get the effect of environment variables without needing changes in the current runtime.

ForbesLindesay commented 8 years ago

@lacker the problem is that NODE_ENV is effectively reserved for optimisation. You can't use anything other than NODE_ENV.

I once built an app for one client, and ended up deploying a second identical copy of the app for a different client. The only difference between the two deployments was the company names and branding colours. To do this, I just defined environment variables for each of those features. The issue is that those features vary between different clients, not between development and production.

ForbesLindesay commented 8 years ago

@gaearon I don't like the idea of a prefix. It feels awkward to have to prefix all the variables with something that has nothing to do with my app, and counter-intuitive that some variables pass through and others do not.

gaearon commented 8 years ago

Can we use npm config for specifying variables like port, proxies, etc (need to figure out a list of them), as well as exposing custom options to the app? The nice thing about it is it can be specified right in package.json, there’s a CLI to change it, and you can have local overrides or use environment variables... It seems.

gaearon commented 8 years ago

By the way I’m coming to the conclusion that specifying ports, proxies and stuff is fair game because we’re competing for system resources. We just need to make sure the defaults work well with zero configuration, but some optional config for shared resources like ports is fine.

ForbesLindesay commented 8 years ago

I'd suggest for port number we should just use process.env.PORT || 3000, which is pretty much the standard in node.js. It's what platforms like heroku support.

gaearon commented 8 years ago

Port would only be relevant for local development, wouldn’t it?

The reason I’d like to avoid envs by default is that you can’t easily write scripts that use them in a cross platform way. So somebody publishes a react-example that contains PORT=3001 react-scripts start, and then somebody else tries this on Windows, and it doesn’t work. Now they can’t learn React from this example. A solution to this is to use a package called cross-env (or to extract a script and set the process.env.PORT yourself there) but this is not a very common knowledge.

So I would prefer config in package.json because it is cross-platform “by default” but you can also specify envs to override it if you really want to.

gaearon commented 8 years ago

For those of you who just needed PORT, you can specify it as an environment variable in the next alpha. Please try it out: https://github.com/facebookincubator/create-react-app/pull/190.

This is not related to this issue so keeping it open.

eliperelman commented 8 years ago

Neo solves this using an env namespace, like @gaearon proposed. I think that would be preferred over setting config in package.json since some items may be environment specific, and others may have needs for access of sensitive data which should not be committed.

gaearon commented 8 years ago

Thanks for the insight! Has this been working well for your users?

eliperelman commented 8 years ago

@gaearon yes, we have a few different projects that utilize custom namespaced environment variables. I'll open a PR and we can evaluate its merits.

unscene commented 8 years ago

I am in favor of passing env to DefinePlugin. In our use case we end up swapping out a bunch of different service urls. Writing code like:

if (process.env.NODE_ENV === 'development') {
  // export different stuff here
} else {

}

to do something that environment variables solve seems suboptimal.

gaearon commented 8 years ago

@unscene

Support for custom variables has been implemented in #342. It will be released in 0.3.0. Support for NODE_ENV has been in Create React App since the very first version.

unscene commented 8 years ago

Sweet that sounds great.

As for NODE_ENV I saw it were there just didn't think it was the way to go.

Dig the name-spacing solution though.

thg303 commented 8 years ago

Took me half an hour to find out this: To set a different port for create-react-app you have to create ".env" file in project root directory and set the port like this: PORT=80

tannerlinsley commented 8 years ago

@thg303 and posterity, note that to use a port below 1024, you'll need to sudo your react-scripts start command.

eric-khoury commented 8 years ago

@thg303 Did it work for you just by adding the .env file with the env variable? I'm having trouble getting that to work.

thg303 commented 8 years ago

@eric-khoury I just test it for changing the port number. I have no idea for other env variables.

thien-do commented 8 years ago

@eric-khoury Please note that only env that starts with REACT_APP_… available in your app. Every other envs (such as PORT) only available in build process

eric-khoury commented 8 years ago

@dvkndn Thx for the info, I missed that part completely.

ORESoftware commented 7 years ago

Simple question @all, how can we inspect the value of NODE_ENV in the front-end React code? I assume this is passed to the front-end given this conversation?

Is there a way to set a default NODE_ENV on the server as well? Perhaps by editing the .env file? I assume you have to npm run eject to do that? Or not?

unscene commented 7 years ago

You could always just alias it with the REACTAPP prefix if it's not available


From: Operations Research Engineering Software+ notifications@github.com Sent: Dec 12, 2016 9:53 PM To: facebookincubator/create-react-app Cc: Ryan Fairchild; Mention Subject: Re: [facebookincubator/create-react-app] A way to define custom environment variables (#102)

Simple question @allhttps://github.com/all, how can we inspect the value of NODE_ENV in the front-end React code? I assume this is passed to the front-end given this conversation?

- You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/facebookincubator/create-react-app/issues/102#issuecomment-266621353, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AADdT4RzoEf5ogcwNpKbKnzb05z034rtks5rHgi0gaJpZM4JTJJ0. This e-mail message, and any attachments, is intended only for the use of the individual or entity identified in the alias address of this message and may contain information that is confidential, privileged and subject to legal restrictions and penalties regarding its unauthorized disclosure and use. Any unauthorized review, copying, disclosure, use or distribution is strictly prohibited. If you have received this e-mail message in error, please notify the sender immediately by reply e-mail and delete this message, and any attachments, from your system. Thank you.

eliperelman commented 7 years ago

@ORESoftware could you use console.log(process.env.NODE_ENV)?

thg303 commented 7 years ago

@ORESoftware you have to set NODE_ENV via os environment variable. in linux it would be like executing this command in the terminal: NODE_ENV=production && npm run build

ORESoftware commented 7 years ago

@thg303 thanks, I got that part :) what I am not sure of, is if this value is sent by create-react-app or if I have to do that manually. or any other env values besides NODE_ENV

If it is sent to the front-end, how can I access the value for NODE_ENV on the front-end sent from the backend?

Basically looking to render things differently depending if I am dev or prod, etc.

gaearon commented 7 years ago

Simple question @all, how can we inspect the value of NODE_ENV in the front-end React code? I assume this is passed to the front-end given this conversation?

Have you had a chance to read the corresponding section in the User Guide? It specifically answers this question with an example. TLDR: it’s process.env.NODE_ENV. It is replaced at the build time so you get "development" on npm start but a production bundle created with npm run build will hardcode this to "production".

If it is sent to the front-end, how can I access the value for NODE_ENV on the front-end sent from the backend?

You can't "send" it from backend. If it's on the backend, your app is already compiled in production mode, so it's "production".

you have to set NODE_ENV via os environment variable. in linux it would be like executing this command in the terminal:

This answer is incorrect. With Create React App NODE_ENV is hardcoded depending on whether you used npm start or npm run build. You can't override it (precisely because people sometimes override it wrongly and end up with bloated builds since they get a slow development version of React). However you can specify REACT_APP* variables, as described in User Guide I linked to above in this comment.

ORESoftware commented 7 years ago

@gaearon so basically in short, you can do:

REACT_APP_NODE_ENV=development

and on the front end, we have:

const nodeEnv = window.REACT_APP_NODE_ENV`   // (likely incorrect) 

but looking at the link, you sent, it looks like:

render() {
  return (
    <div>
      <small>You are running this application in <b>{process.env.NODE_ENV}</b> mode.</small>
      <form>
        <input type="hidden" defaultValue={process.env.REACT_APP_SECRET_CODE} />
      </form>
    </div>
  );
}

that would mean that instead of

const nodeEnv = window.REACT_APP_NODE_ENV`   // front-end

instead on the front-end we have

const nodeEnv = process.env.NODE_ENV`      //front-end

It's a little counterintuitive to have Node.js related stuff on the front-end, but I guess that's pretty normal now that we are using Webpack etc. We have to use surrogates for some of the backend code (like process, fs, etc).

thanks

gaearon commented 7 years ago

@ORESoftware

No, NODE_ENV is the built in one. It's the only variable that is available by default and it doesn't need REACT_APP prefix. You read it like this:

var env = process.env.NODE_ENV;

As I already mentioned, User Guide contains an example that specifically shows this. I recommend giving it another look.

For any custom variables, you'd use REACT_APP prefix. But NODE_ENV is built-in, doesn't have a prefix, and you can't specify it yourself—its value depends on whether you're in development or a production mode.

Please refer to the section in User Guide, I'm just repeating what it says here.

ORESoftware commented 7 years ago

@gaearon ok I just read what you wrote, np. However the user guide does not seem to demonstrate how to access the variables in the front-end, at least not in the same section (tell me if I am wrong :)

I have this

 "start-dev": "REACT_APP_NODE_ENV=development && react-scripts start",

on the front-end

if I log process.env, I get an empty object. So I am guessing that instead of doing

process.env.REACT_APP_NODE_ENV

it will be

window.REACT_APP_NODE_ENV

I will test it out, but if it's it not in the docs yet, I would be happy to add whatever the right way to do this is.

e.g. window.R = no search results

screenshot from 2016-12-13 14-28-27