Skip to content
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

Confused about usage of process.env #22266

Closed
damianon opened this issue Feb 17, 2021 · 14 comments
Closed

Confused about usage of process.env #22266

damianon opened this issue Feb 17, 2021 · 14 comments
Labels
bug Issue was opened via the bug report template.

Comments

@damianon
Copy link

What version of Next.js are you using?

10.0.5

What version of Node.js are you using?

14

What browser are you using?

Firefox

What operating system are you using?

Windows

How are you deploying your application?

cross-env NODE_ENV=production node server.js

Describe the Bug

I'm very confused about the use of process.env. Variables.
(Here in the docs)[https://nextjs.org/docs/basic-features/environment-variables] it says 'In order to keep server-only secrets safe, Next.js replaces process.env.* with the correct values at build time.' However looking into my docker container (or the locally compiled code) in the .next folder i can clearly still see process.env.VAR_NAME in all the places, and runtime environment variables are used. (all our pages are serverside rendered, not statically)

While this is mostly what I want, there are two cases where i want build time fixed variables, so looking into it, it's not very clear how this can be achieved.

Also under the (runtime variables docs)[https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration] it suggests using serverRuntimeConfig/publicRuntimeConfig together with getInitialProps which however is aparently deprecated in favor of getServersideProps. Also when I tried using this mapping in the next.config.js using the next config on a page always returned both of these as empty arrays (public and server).

So I have as of now no clue at all on how to achieve both runtime and build time environment variables. So far in my project process.env. seems to be runtime and serverRuntimeConfig didn't work at all.

If process.env. is in fact runtime, that's fine, but then the docs are wrong in stating it's build time.

server.js is just a express wrapper for next:

app.prepare().then(() => {
    const server = express();

    server.use(
        /api/,
        createProxyMiddleware({
            target: process.env.API_ENDPOINT,
            changeOrigin: true,
        })
    );

    server.all('*', (req, res) => handle(req, res));

    server.listen(port, (err) => {
        if (err) throw err;
        console.log(`> Ready on http://localhost:${port}`);
    });
});

Expected Behavior

A clear way on how to use environment variables defined at build time and at runtime. (e.g. i want the version to be fixed at build time, but some connection strings at runtime)

To Reproduce

Use process.env. variables somewhere in pages or components.
run next build
inspect .next generated code and still find process.env (even if the docs state the opposite)

@damianon damianon added the bug Issue was opened via the bug report template. label Feb 17, 2021
@damianon
Copy link
Author

damianon commented Feb 17, 2021

Okay, after some more tests it seems to me that the following is the case:

IF a NEXT_PUBLIC_ env variable is present at build time, it will be replaced in the code.
If not and for the not public process.env. they are left in place and not replaced, therefore making them into runtime variables.

But in NODE_ENV=production NEXT_PUBLIC_Vars are not available client side (can see the runtime value in the serverside rendered page, but client rerenders it as undefined and thus empty)

@ScorpIan555
Copy link

Thanks for posting this. I think I've seen the same thing, but from a different angle. I'm using Typescript in one of my projects and when I went to replace a couple things in my GraphQL client-side config with process.env.NEXT_PUBLIC_foo, it was generating an compiler error/warning because it was showing the type of process.env.NEXT_PUBLIC_foo as 'string | undefined'.

Having read this, I'm wondering if ....

@skrhlm
Copy link

skrhlm commented Feb 19, 2021

I was frustrated about this and made a repo that clarifies the differences:
https://github.com/skrhlm/NEXT-JS-ENV-VARS

Also worth noting that I made a small issue of this, as the reason the above i confusing is because the word env variables is actually not accurate.
#22343

@eric-burel
Copy link
Contributor

eric-burel commented Feb 25, 2021

There is something still unclear to me: what about injecting global variables in the frontend code during build. For instance, if you want to access the current application version from package.json in the frontend, you have two ways:

  • Using getStaticProps/getInitialProps, read package.json version, pass it to the app/page, and add this variable in the React context. Quite overkill for configuration variables.

  • Using Env variable, you need to use the old pattern of setting env in next.config.js. This file is run at build time, so it's a safe place to read package.json. But this forces you to use the "deprecated" old approach: https://nextjs.org/docs/api-reference/next.config.js/environment-variables.
    Because you don't know the variable in advance, you can't hard-code it in a file, you want it to be computed during the build step.

  • Use runtime config, but you lose static optimization: https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration

  • Use some Webpack magic, necessary if your variable changes between server and client:

    // Define an environment variable so source code can check whether or not
    // it's running on the server so we can correctly initialize Sentry
    config.plugins.push(
      new options.webpack.DefinePlugin({
        'process.env.NEXT_IS_SERVER': JSON.stringify(
          options.isServer.toString()
        ),
      })
    )

This code is taken from Sentry official example, that uses a combination of those patterns: https://github.com/vercel/next.js/blob/canary/examples/with-sentry/next.config.js

@salazarm
Copy link

salazarm commented Apr 8, 2021

Would be nice to support this while not completely opting out of static optimization to set some variables once during server start

@eric-burel
Copy link
Contributor

In the meantime I've written an article that details more precisely the various ways we can load configuration, from env or computed dynamically: https://blog.vulcanjs.org/how-to-set-configuration-variables-in-next-js-a81505e43dad
Regarding runtime config, you could simply use a JS object for the same result.

@danielsimao
Copy link

In the meantime I've written an article that details more precisely the various ways we can load configuration, from env or computed dynamically: https://blog.vulcanjs.org/how-to-set-configuration-variables-in-next-js-a81505e43dad
Regarding runtime config, you could simply use a JS object for the same result.

Thats a great explanation Eric! Thanks a lot for the article. Cheers

@ZuBB
Copy link

ZuBB commented Jun 7, 2021

just wanted to share my experience with this Runtime configuration.

here is my case: our app is integrated with another our "3rd-party" app. each app has its own set of envs which are used for deployments. our intention was to build our app once and it should consume right URL to specific env of 3rd-party app during runtime.

in another words

jenkins@ci $: next build
...
docker@{env1}: $ _3RDP_URL="http://1.com" next start
...
docker@{env2}: $ _3RDP_URL="http://2.com" next start

and it does not work, because all of .env* files are actually incorporated into bundle at build time. and under 'runtime' nextjs team probably means all that happens in browser, but not what happens during execution of next start

probably runtime should be renamed to boot-time or startup-time (if it would work that way)

@exocode
Copy link

exocode commented Aug 6, 2021

I've also stumbled over that issue:

I tried the all options the docs are saying here, which was very frustraiting:
https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration
and here
https://nextjs.org/docs/basic-features/environment-variables

next.config.js, .env, --build-args, process.env.NEXT_PUBLIC_VAR, process.env.VAR

Last but not least I got it working, I suggest updating the docs, or make it more clear what's the best approach to deploy and use in "2021".

What definitely didn't work for me was the next.config.js approach: the results were empty or undefined:
Maybe I am using this wrong, and someone can give me a hint...

I set .env

API_URL=http://localhost:3000
NEXT_PUBLIC_API_URL=http://localhost:3000

my next.config.js

module.exports ={
    // your config for other plugins or the general next.js here...
    // Will only be available on the server side
    serverRuntimeConfig: {
        demo_api_url: process.env.API_URL,
    },
    // Will be available on both server and client
    publicRuntimeConfig: {
        demo_public_api_url: process.env.NEXT_PUBLIC_API_URL,
    },
};

Then in my ApiClass:

import getConfig from 'next/config'
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig()
const PublicApiUrl = process.env.NEXT_PUBLIC_API_URL;
const ApiUrl = process.env.API_URL;

const shopApi = {
    getProducts: (options: GetOptions = {}): Promise<Product[]> => {

        console.log("PublicApiUrl alias:", PublicApiUrl);
        console.log("API_URL:", process.env.API_URL);
        console.log("NEXT_PUBLIC_API_URL:", process.env.NEXT_PUBLIC_API_URL);
        console.log("publicRuntimeConfig:", publicRuntimeConfig);
        console.log("serverRuntimeConfig:", serverRuntimeConfig);
        console.log("demo_public_api_url:", publicRuntimeConfig.demo_public_api_url);
        console.log("demo_api_url:", serverRuntimeConfig.demo_api_url);

        return fetch(`${PublicApiUrl}/products/index.json`).then(
            (response) => response.json()
        );

This is the console output SERVERSIDE:

PublicApiUrl alias: http://localhost:3000
API_URL: http://localhost:3000
NEXT_PUBLIC_API_URL: http://localhost:3000
publicRuntimeConfig: {}
serverRuntimeConfig: {}
demo_public_api_url: undefined
demo_api_url: undefined

@eric-burel
Copy link
Contributor

eric-burel commented Aug 7, 2021

@exocode The serverRuntimeConfig thing is useless, because you just need to create a JSON object whose field is defined based on a environment variable => best approach is to "just do it", create something named serverConfig.js and export an object from there.
For the publicRuntimeConfig, same idea, you can create a publicConfig.js file that does the same, the only difference with server-side is that you must stick to NEXT_PUBLIC_* environment variables as you already did, since Next protects developers against private variables leaking in the client bundle thanks to this naming convention.

@balazsorban44
Copy link
Member

Only client-side values are inlined as documented here:

https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser

@damianon
Copy link
Author

damianon commented Dec 22, 2021

Edit: Sorry, misunderstood your answer.

But that's the thing. According to the article they are inlined in the code that is send to the browser. Meaning that I can use runtime env vars on the system it is running on. But that doesn't work as mentioned above. The values are not present client-side

@skrhlm
Copy link

skrhlm commented Dec 22, 2021

@damianon when static optimization occurs things are bundled during buildtime. Opt-out of using getStaticProps and add a getInitialProps in your _app to completely remove this behaviour. Then you set env-variables on your host and refer to these in next.config.js, where they are easy to reassign and scope to either publicRuntimeConfig or serverRuntimeConfig.

As a sidenote this took me forever to figure out, the next documentation is not clear at all on how to actually get this to work.

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

No branches or pull requests

9 participants