I expected process.env.NODE_ENV on the client/browser to correspond to environment variable set when app is run:
package.json
"scripts": {
"dev": "next",
"staging": "NODE_ENV=staging next",
"build": "next build",
"start": "next start"
}
Expected process.env.NODE_ENV
to be 'staging'
when running npm run staging
:
components/Page.js
class Page extends React.Component {
componentDidMount () {
console.log('process.env.NODE_ENV', process.env.NODE_ENV) // expected 'staging' to be printed here when running `npm run staging`
console.log('process.env.BUILD_VERSION', process.env.BUILD_VERSION) // '1.0.0'
}
render () {
...
}
}
console.log(process.env.NODE_ENV)
prints 'development'
on the client/browser with npm run staging
regardless of the actual NODE_ENV value.
I've attempted to override the default DefinePlugin
webpack config here to no avail:
next.config.js
module.exports = {
webpack: (config, { dev }) => {
config.plugins.push(
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.BUILD_VERSION': JSON.stringify(pkg.version)
})
)
return config
}
}
It seems that process.env.NODE_ENV
is set to 'development'
or 'production'
based ondev
in the default webpack config.
How can I override this behaviour and have process.env.NODE_ENV
on the client reference the actual NODE_ENV
value?
I would like to set app variables based on NODE_ENV on the client
| Tech | Version |
|---------|---------|
| next | ^4.4.0-canary.3 |
| node | 6.11.5 |
| OS | macOS siera 10.12.6 |
| browser | Google Chrome Version 63.0.3239.132 |
Heh, interesting... I will add to the conversation in that this is NOT an issue for me running a custome server. Env vars behave as expected. I presume this is only an issue when running server using next start
.
We have examples on how to pass env variables to your app code:
Hi @jflayhart, I tried it with a custom server but still could not have env vars behave as expected on the client/browser. They are correct on the server though. Not sure what I've done wrong.
"scripts": {
"staging": "NODE_ENV=staging node server.js"
}
npm run staging
server.js
const express = require('express')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare()
.then(() => {
const server = express()
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port} [${process.env.NODE_ENV}]`) \\ prints `staging` correctly
})
})
@sergiodxa Hi, I could pass env variables successfully to the app code, but could not get NODE_ENV
on the client to reference the actual value NODE_ENV
that the app was run with. Running NODE_ENV=staging node server.js
, process.env.NODE_ENV
is 'development'
when accessing it in pages instead of 'staging'
.
Really appreciate any help, do you have any idea what I might have done wrong?
Check the examples, you need to to extra steps to pass the env variables to your app code.
Hi @sergiodxa which example were you talking about? This one, https://github.com/zeit/next.js/tree/canary/examples/with-dotenv ?
I don't think the example helps.
As to process.env.NODE_ENV
, this line overrides its value for the Client, so it would be either development
or production
.
Any suggestion to pass the real NODE_ENV value to the Client?
As to process.env.NODE_ENV, this line overrides its value for the Client, so it would be either development or production.
Looking at this comment on your PR @haohcraft , it looks like there's no way to set NODE_ENV
to anything but production
or development
Looking @ this PR, it seems any config for environments besides 'production'
and 'development'
have to be implemented in next.config.js
Is this correct @sergiodxa ?
@brandonmp yeah my comment here: https://github.com/zeit/next.js/pull/3889#discussion_r170528404 explains why.
The configuration PR is already available in next@canary
馃憤
We changed the signature of it though:
Check the docs for it in the readme here: https://github.com/zeit/next.js#exposing-configuration-to-the-server--client-side
Just to explain on this issue:
Setting NODE_ENV
to staging
makes no sense whatshowever since staging
is not known by any packages, including React. Your staging environment should be as close as possible to production, meaning same build, same NODE_ENV. Just to give you an example, if you don't set NODE_ENV=production React-dom SSR will be started in development mode, causing it to handle a lot less requests per second.
If you want to read specific environment values you can use the new publicRuntimeConfig
as explained above 馃憤
Additionally: here is the React thread on it: https://github.com/facebook/react/issues/9245
@timneutkens I understand the choice, but yet disagree with it.
An app can have multiple environments. But a package can only manage so many, usually only "development" and "production", and that's totally fine.
I agree that the staging environment must be as close as possible as the production one. But that's not for you to decide how I should run my app. If I want/need to use a NODE_ENV=staging then a library shouldn't override this behaviour.
This solution is inelegant, and leads to different issues. By making such an assumption that staging=production, you actually create a broken state within the same app.
When powering next.js with a express.js server, you end up with express saying "staging" while next.js replies "production", this is, in my opinion, far worse than having React-dom SSR performing slower in staging than in production mode.
There are more elegant solutions to this issue, for instance:
development_like
and production_like
feature, basically an array of strings which are used when the NODE_ENV is unknown, this would probably be one of the most elegant solutions.production_like = ['staging']
, Next.js app would provide "production" instead of "staging" to packages relying on those environment variables to perform correctly.Using publicRuntimeConfig
is a workaround, but it's just too hacky to my taste to be honest, having half my app saying "staging" and the other half saying "production" just doesn't feel right.
There may be other alternative solutions (more elegant too?), I just wrote the first that came to me.
@timneutkens What do you think about it?
Also, since the publicRuntimeConfig workaround doesn't work in my case, this is a very blocking issue.
Hey Any update regarding this issue? I just see many closed issues without any constructive solutions. I am really considering moving out of next.js due to too many closed issue without really solving the problem
Here is what I eventually did. It's hacky and smells but I didn't find a better alternative back then.
utils/env.js
import getConfig from 'next/config';
/**
* XXX Next.js FORCES the NODE_ENV to be either "development" or "production" at build time.
* Because of this, we have a difference between the process.env.NODE_ENV given by Express and the on given by Next
* In order to avoid this huge issue, we stored the real NODE_ENV in next.runtimeConfig.js:NODE_ENV
* And this function must be used to get the NODE_ENV instead of process.env.NODE_ENV
*
* This function is compatible with Express/Next, and can be used anywhere, on the client and server.
*
* @returns {string}
* @see XXX https://github.com/zeit/next.js/issues/3605#issuecomment-370255754
*/
export const getNodeEnv = () => {
const { publicRuntimeConfig } = getConfig();
const realNodeEnv = publicRuntimeConfig.NODE_ENV;
return realNodeEnv || process.env.NODE_ENV;
};
next.config.js
// const { PHASE_DEVELOPMENT_SERVER } = require('next/constants');
const { serverRuntimeConfig, publicRuntimeConfig } = require('./next.runtimeConfig');
module.exports = (phase, { defaultConfig }) => {
return {
serverRuntimeConfig,
publicRuntimeConfig,
webpack: (config, { buildId, dev, isServer, defaultLoaders }) => {
// XXX https://github.com/evanw/node-source-map-support/issues/155
config.node = {
fs: 'empty',
module: 'empty',
};
return config;
},
};
};
next.runtimeConfig.js
const serverRuntimeConfig = {};
const publicRuntimeConfig = {
GROUP_NAME: process.env.GROUP_NAME,
NODE_ENV: process.env.NODE_ENV, // XXX Used in utils/env
};
module.exports = {
serverRuntimeConfig,
publicRuntimeConfig,
};
Usage:
const env = getNodeEnv();
getNodeEnv()
works client and server side this way. I don't use process.env.NODE_ENV
but getNodeEnv()
instead.
I think this workaround is a pure waste of brain cells and the issue is coming from Next.js's design. Also, I guess it decreases the local performances @timneutkens was talking about, but I honestly don't care about that because it just works.
@timneutkens I believe this issue should be re-opened and a proper fix should be implemented considering how many upvotes my last comment has received. Having to rely to such hacky ways to make things work isn't a good thing, it really adds a complexity layer.
found a better way to use process_env
on the front end.
use getInitialProps()
```js
class app extends Component {
static async getInitialProps() {
const node_env = process.env.NODE_ENV
return { node_env }
}
// stuff....
render() {
console.log(this.props.node_env) // should show node environment.
}
}
reason is because before the component renders, it loads initial props from the server end, so thats where we can capture the process.env and place it in a variable to be returned once component/page load starts to construct
@qwerqy how does this work with routing?
The initial Page load will use the server value and move it into the frontend via __NEXT_DATA__
. But once you navigate on the page with <Link>
the following page (including its initial props) will be evaluated in the browser, so prodess.env
does not exist anymore.
For people wondering how to use different URLs, APIs etc for different environments, check out this medium article.
It solved the issue I was having https://medium.com/chotot/nextjs-multiple-environment-builds-e8b2ccb11c04
I got the same problem, and I had a workaround if it could be that.
At beginning, I offer a environment XXX-FOR-QA for QA which runs online and requests test API.
"scripts": {
"build": "next build",
"start": "next start",
"XXX-FOR-QA": "cross-env NODE_ENV=pre node next-server.js"
},
After run 'npm run build', I launched the server by XXX-FOR-QA, in server I got the NODE_ENV was "pre", but in app the NODE_ENV was "production".
In server.js, I get the request of index.js. Then pass the 'env' by query.
server.get('/', (req, res) => {
const actualPage = '/'
const queryParams = {
env: process.env.NODE_ENV
} // process.env.NODE_ENV is pre
app.render(req, res, actualPage, queryParams)
})
In function getInitialProps of index.js, I could have the same value as env in server.js
static async getInitialProps({ query }) {
console.log(query.env) // it's 'pre'
}
I hope it can help you guys, if you have better solutions, please let me know, Thanks.
Most helpful comment
@timneutkens I understand the choice, but yet disagree with it.
An app can have multiple environments. But a package can only manage so many, usually only "development" and "production", and that's totally fine.
I agree that the staging environment must be as close as possible as the production one. But that's not for you to decide how I should run my app. If I want/need to use a NODE_ENV=staging then a library shouldn't override this behaviour.
This solution is inelegant, and leads to different issues. By making such an assumption that staging=production, you actually create a broken state within the same app.
When powering next.js with a express.js server, you end up with express saying "staging" while next.js replies "production", this is, in my opinion, far worse than having React-dom SSR performing slower in staging than in production mode.
There are more elegant solutions to this issue, for instance:
development_like
andproduction_like
feature, basically an array of strings which are used when the NODE_ENV is unknown, this would probably be one of the most elegant solutions.By using
production_like = ['staging']
, Next.js app would provide "production" instead of "staging" to packages relying on those environment variables to perform correctly.This way, you don't override anything, you just provide the best match to Next.js dependencies, it is configurable by the developper, and doesn't lock anybody.
Using
publicRuntimeConfig
is a workaround, but it's just too hacky to my taste to be honest, having half my app saying "staging" and the other half saying "production" just doesn't feel right.There may be other alternative solutions (more elegant too?), I just wrote the first that came to me.