-
Notifications
You must be signed in to change notification settings - Fork 27.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Accessing environment variables in browser #159
Comments
As far as I tested, it works fine. // package.json
{
"scripts": {
"start": "MY_VAR=hi next",
}
} // pages/index.js
const env = 'undefined' !== process ? process.env.MY_VAR : null
export default class extends React.Component {
static getInitialProps () {
return { env }
}
render () {
return <div>{this.props.env}</div>
}
} I did it like above.
|
OK I'm new to SSR and just realized my need is more complicated. I need to access a So this environment variable needs to be accessible on both client and server code. |
Sorry it's not related to Apollo etc, but is because I wrap a component in a higher-order component (something also new to me). I don't know how to access the variable from the wrapped component. The following renders an empty page instead of "hi": // package.json
{
"scripts": {
"start": "MY_VAR=hi next",
}
} // components/Hoc.js
import React from 'react';
export default function hoc(WrappedComponent) {
return class Hoc extends React.Component {
render() {
return <WrappedComponent {...this.props} />;
}
};
} // pages/index.js
import React from 'react';
import hoc from '../components/Hoc';
const env = process.env.MY_VAR;
class Index extends React.Component {
static getInitialProps() {
return { env };
}
render() {
return <div>{this.props.env}</div>;
}
}
export default hoc(Index);
// above doesn't work, but `export default Index;` renders "hi" properly |
@sedubois you can globally access For accessing environment variables from client, you can embed them to DOM and retrieve later like the following. const { MY_VAR } = 'undefined' !== typeof window ? window.env : process.env
export default () => {
return (
<div>
{MY_VAR}
<script dangerouslySetInnerHTML={{ __html: 'env = ' + escape(JSON.stringify({ MY_VAR })) }}/>
</div>
)
} Actually, your real code would become more complicated to not render |
@sedubois Because the // components/Hoc.js
import React from 'react';
export default function hoc(WrappedComponent) {
return class Hoc extends React.Component {
static getInitialProps(ctx) {
return WrappedComponent.getInitialProps(ctx)
}
render() {
return <WrappedComponent {...this.props} />;
}
};
} |
@dstreet Your answer seems correct! Thanks. Feel free to reopen if you still have the problem. |
There's also https://github.com/mridgway/hoist-non-react-statics which is in general a good idea when creating HOCs. |
@nkzawa I don't know how to access the env var in the browser-side It's in |
Could we just add this to the Webpack config?
The XXX/YYY should be e.g configured in the package.json. Then the environment variables will be substituted for their values and therefore accessible browser-side? If you agree I can try to add that to the Webpack config... |
Or maybe just wait for #174, then I can add it myself. Although once again I wouldn't wish other users to get into the same surprises and need to figure all of this out. |
@sedubois you can embed env vars to dom like #159 (comment) .
|
For the moment I managed to do it using a HOC and using it on every page. Probably would be nicer with // withApiHoc.js
import React from 'react';
import Axios from 'axios';
const endpoint = process.env.API_ENDPOINT;
export default (WrappedComponent) => {
return class extends React.Component {
static getInitialProps(ctx) {
let props = {};
if (WrappedComponent.getInitialProps) {
props = { ...WrappedComponent.getInitialProps(ctx) };
}
return {
...props,
endpoint,
}
}
render() {
return (
<WrappedComponent
{...this.props}
api={
Axios.create({
baseURL: this.props.endpoint + '/api/v1/',
})
}
/>
);
}
};
} |
this will only work in the first request, right? because only the first one is server rendered and has access to process.env |
@luisrudge yep, so you have to embed env vars among all pages and enable to access these data when navigating on client. |
@nkzawa you're also exposing the env var to the browser when embedding it in the Also, although it renders properly I got console errors when trying to run your code:
(last error is thrown by the very first line of code) |
@sedubois ah yeah, true. You can use |
@nkzawa thanks, I'll just wait for the configurable Webpack for now. |
The HOC wasn't something I wanted (i wanted to extend a layout instead, for an unrelated reason). I ended up with this... basically the import React from 'react';
import 'isomorphic-fetch';
import segment from '../lib/segment';
class PageLayout extends React.Component {
static async getInitialProps ({ req }) {
if (req) {
const configUrl = process.env.CONFIG_URL;
const res = await fetch(configUrl);
const data = await res.json();
return data;
}
return {};
}
componentWillMount () {
if (typeof window !== 'undefined') {
segment(); // my analytics snippet
const analytics = window.analytics;
if (analytics.load && this.props.segmentKey) {
analytics.load(this.props.segmentKey);
}
analytics.page(); // marks a pageview, runs every time.
}
}
}
PageLayout.propTypes = {
segmentKey: React.PropTypes.string,
};
export default PageLayout; Does this make sense? Would love feedback and more importantly, if you're using this approach, please let me know. 😄 I think you could also inject the variables into the window object / into a custom dom node / into something right as the |
In Next 2.0, you can do something like this in your const webpack = require('webpack');
module.exports = {
webpack: (cfg) => {
cfg.plugins.push(
new webpack.DefinePlugin({
'process.env.CUSTOM_VALUE': JSON.stringify(process.env.CUSTOM_VALUE),
})
);
return cfg;
},
}; Since Next runs Webpack to build the code before running it on the client and server, the JS will be embedded with the env values from build-time. (Having to |
@ericf Is this still in the works? I am using the exact same snippet you posted and don't see any vars under |
@purplecones yeah it has been with the latest beta. Did you make sure to run your build with the env vars set? I'm using the |
@ericf I also can't seem to get this to work on beta.5. Identical webpack config to what you included |
@purplecones @jbaxleyiii you should const webpack = require('webpack');
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
module.exports = {
webpack: (config) => {
config.plugins.push(
new webpack.DefinePlugin({
'process.env.FB_APP_ID': JSON.stringify(process.env.FB_APP_ID),
'process.env.FB_PAGE_ID': JSON.stringify(process.env.FB_PAGE_ID),
})
);
return config;
},
}; |
@ericf I used ur exact webpack config. const webpack = require('webpack');
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
module.exports = {
webpack: (config) => {
config.plugins.push(
new webpack.DefinePlugin({
'process.env.GRAPHQL_URL': JSON.stringify(process.env.GRAPHQL_URL),
})
);
return config;
},
}; The variable is visible on the server side when I log
but not on the client: This is my .env
Using [email protected] I must be missing something else. |
The define plugin does replacements. It replaces |
@ericf so how do I get |
@purplecones add a console.log(process.env.foo) and you'll see that it should work. If you go and inspect the source code, you'll see that it will have been replaced with console.log("FOO") (or whatever the value of process.env.foo). You can check for webpack.DefinePlugin docs and examples elsewhere if needed. But personally so far I've been now using (in my project https://github.com/relatenow/relate) a JSON config file. Might switch to env vars of the need arises. |
The issue with using define plugin is its setting the env variable at BUILD time and not RUN time. This can break some workflows that use CI/CD to build units. |
@andrewmclagan Right, and this violates 12-factor, for those who care about such things. Instead you can have an env.js that is referenced in a index.html script tag before the app script tag that loads variables onto, say, window.env. Just make sure this file finds its way to the right spot at runtime via your favorite method (including having your server serve it). Also, if you're minifying, you'll want to read your variables as string keys, e.g. |
refferring to the dotenv example https://github.com/zeit/next.js/tree/v3-beta/examples/with-dotenv I have a a use case where i have a .env.dev .env.stage .env.production however when I build my stage build i cannot stop the babel or next configuration from using the production vars. Has anybody else come across this? I am using babel-plugin-inline-dotenv I intend to pass the env to the top level package.json script and then load the .env through the .babelrc file
I have also attempted to use inside my next.config.js if (process.env.NODE_ENV === 'stage') { as well as in my index.js require('dotenv').config({path: '/.env.stage'}); no luck, any suggestions on supporting multiple environment variables? appended after edit: It would appear the belrc is overwriting any next configuration. I am trying to remove babelrc file now and see if I can configure using only the next.config |
Hi @neverfox, @andrewmclagan, and everyone else, We found a solution to maintaining 12-factor app guidelines by proxying the API from the front-end to the backend via http-proxy-middleware. Start by bringing out a custom server.js file. Then add a new endpoint to proxy the API:
And now when you
Note that we check We use Now when you change the This is all done without using shitty webpack plugins/hacks at the cost of having to proxy your GraphQL requests via your front-end server. For 99% of people this should not matter much performance wise. Hope this helps! |
@ericf i tried your webpack/dotenv suggestion and i cannot get it to work. we are also proxying other env vars similar to what @huangbong is doing so the server-side part works fine. but this other variable is more a feature flag that we want to set that would affect how some views are presented. what else should i run after i add those lines to |
@Kielan i managed to make it work following the with-dotenv example. my concern now is if the env vars would be exposed in any way to the user if they know where to look. i just want one variable exposed. from what i see in the browser console, i cannot log the env vars (that is good) and the feature flag does apply (also good). i'm worried that some other file compiled somewhere exposes the rest of the variables. i'm new to nextjs and react in general so this may sound like a stupid question. edit: it actually didn't work... must have been some remnant cache file… so i am back to square one :\ edit 2: ok so it works if i remove and reinstall the babel plugins, which seems odd. if i change the env var value alone and re-run (using yarn, |
ok i think i solved by using the server-side aspect of |
The I tried like about a 10 combinations of tries and nothing, also the help? |
@sebas5384 I believe (if I'm not mistaken) webpack replaces references to |
@zenflow make sense, but it seems like that's true only at build step, right? something like:
the variable is loaded at server, but not for client, and that's ok, but it should be a way to whitelist what variables could be exposed, not only at build but in run time too. btw thanks for the help @zenflow 👍 |
@sebas5384 did you try with
then i use it in a component: as i mentioned, the value of the variable somehow remains even if i change it in the environment and i can only refresh it via uninstalling and reinstalling the plugins. but this is not an issue for me since the value will be constant in production |
I'm aware that secrets shouldn't be leaked in the browser (#107), but it would be good to have access to some environment variables, e.g a remote graphql endpoint URL in my case. How can environment variables be accessed? A naive
process.env.MY_VAR
doesn't work.The text was updated successfully, but these errors were encountered: