Next.js: Development mode gets access to environment variables it shouldn't

Created on 9 Jul 2019  路  8Comments  路  Source: vercel/next.js

Since we don't stub out process.env you can use runtime environment variable just to have them fail when deployed.

x-ref: https://github.com/zeit/next.js/issues/7827

bug p1

Most helpful comment

I build a next app in a docker image, then deploy that in multiple environments. Those environments have different environment variables, because they differ, for example, in terms of third party client ids. (These environment variables happen to be populated from a kubernetes configmap, but that isn't especially relevant.) It is highly preferable that there only be a single docker image that is portable and not tied to specific config.

Even when using process.env.NEXT_PUBLIC_MY_VAR in my code, and having NEXT_PUBLIC_MY_VAR defined in the environment where I run next start, the value isn't populated on the client side.

How can I get process.env.NEXT_PUBLIC_MY_VAR to actually reflect the environment where it is run, not where it is built?

I'm using Next 9.4.1's built-in environment support.

All 8 comments

Note that changing this is breaking in quite a few apps.

@timer @timneutkens How do you intend to fix this bug? Development mode is another flavor of runtime, so if I have an environment variable in my local dev environment, I would expect it to be assignable to runtime config from process.env in next.config.js.

I think this is a problem in expectations. It is unexpected for Next.js to _execute_ application code during build time as I demonstrated in #8014.

I've noticed some unexpected results when working with publicRuntimeConfig where values are coming from process.env. I can't tell if should be reported separately, or it's part of this issue and can help with debugging, or it's "working as designed" and the https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration docs should be updated to better convey the intended purpose.

I have a situation where I'd like to provide an API_URL to my app via runtime configuration coming from an environment variable. Here is a repo with a readme explaining the steps to reproduce:

https://github.com/robertwbradford/next-runtime-config

If you clone and run the steps, you can see different values being used in different contexts.

I'm mostly confused by Steps 2 and 3 where values are different on client- and server-side renders.

Currently https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration says the following:

A page that relies on publicRuntimeConfig _must_ use getInitialProps to opt-out of Automatic Static Optimization. Runtime configuration won't be available to any page (or component in a page) without getInitialProps.

This seems to imply that you _only need it_ on the pages where you are _using the public runtime config values_. However, Steps 2 and 3 show some "weirdness" when using next/link to navigate from the index.js page which does not have getInitialProps (and would otherwise not need it) to a page that does use the runtime config.

All that being said, some questions...

Question 1: Am I right in thinking that if we are using a process.env.SOMETHING value for a publicRuntimeConfig variable, then we should _not_ be relying on the _build-time_ process.env values and we are expected to always provide a run-time process.env value (i.e., when running next start)?

Question 2: If yes, it seems that Step 4 from my repo is the only reliable setup when using public runtime config where values need to come from process.env. If so, is the following an appropriate/correct change for the docs?

A page that relies on publicRuntimeConfig must use getInitialProps to opt-out of Automatic Static Optimization. Runtime configuration won't be available to any page (or component in a page) without getInitialProps.

If next/link is being used to navigate to such a page, then getInitialProps is also required on the page containing the <Link>. Since this may be difficult to track it is suggested to turn off Automatic Static Optimization by adding a getInitialProps to a Custom <App>.

Also, if assigning a value from process.env to a publicRuntimeConfig variable, that process.env value must be present _at runtime_ (that is, when running next start). You cannot rely on the _build-time_ process.env value.

Kind of a lot here, but I'm hoping it explains the problem and can help with this issue.

cc @ijjk is this fixed with the new env support? Or is this the warning we didn't enable yet?

Specifically referring to the ability to reference a non-NEXT_PUBLIC_ variable in the rendering process (pages dir).

@Timer right, this is the warning we didn't enable yet

I build a next app in a docker image, then deploy that in multiple environments. Those environments have different environment variables, because they differ, for example, in terms of third party client ids. (These environment variables happen to be populated from a kubernetes configmap, but that isn't especially relevant.) It is highly preferable that there only be a single docker image that is portable and not tied to specific config.

Even when using process.env.NEXT_PUBLIC_MY_VAR in my code, and having NEXT_PUBLIC_MY_VAR defined in the environment where I run next start, the value isn't populated on the client side.

How can I get process.env.NEXT_PUBLIC_MY_VAR to actually reflect the environment where it is run, not where it is built?

I'm using Next 9.4.1's built-in environment support.

I build a next app in a docker image, then deploy that in multiple environments. Those environments have different environment variables, because they differ, for example, in terms of third party client ids. (These environment variables happen to be populated from a kubernetes configmap, but that isn't especially relevant.) It is highly preferable that there only be a single docker image that is portable and not tied to specific config.

Even when using process.env.NEXT_PUBLIC_MY_VAR in my code, and having NEXT_PUBLIC_MY_VAR defined in the environment where I run next start, the value isn't populated on the client side.

How can I get process.env.NEXT_PUBLIC_MY_VAR to actually reflect the environment where it is run, not where it is built?

I'm using Next 9.4.1's built-in environment support.

Struggle with it and the temporary solution is using global value like this:

// public/env.js
window.ENDPOINT = 'your-development-request-api'

// .env.development
ENDPOINT = 'your-development-request-api'

// pages/_app.tsx
 <Head><script src='/env.js' crossOrigin='anonymous' /></Head>

// client.ts
export default axios.create({
  baseURL: window.ENDPOINT
});

// package.json
    "predev": "node ./endpoint.js", // run before developing
    "dev": "next",

// endpoint.js
// run `yarn add dotenv -D` first
const path = require('path')
const fs = require('fs')

function write(ENDPOINT) {
  if (!ENDPOINT) {
    console.error('====== read env failuer ========')
    process.exit(1)
  }
  try {
    fs.writeFileSync(path.resolve(path.join(process.cwd(), '/public/env.js')), `window.ENDPOINT = '${ENDPOINT}'`)
  } catch (e) {
    console.error('====== write file failuer ========')
    console.warn(e)
    process.exit(1)
  }
}

// docker container provides `ENDPOINT` environment variable 
// so just write to file
if (process.env.ENDPOINT) {
  write(process.env.ENDPOINT)
} else {
  // while run locally, read .env.development to use development environment variable
  write(require('dotenv').config({ path: path.resolve(process.cwd(), '.env.development') }).parsed.ENDPOINT)
}
# Dockerfile
# https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile.multistage
# Do the npm install or yarn install in the full image
FROM mhart/alpine-node AS builder
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn --frozen-lockfile
COPY . .
RUN yarn build && yarn --production

# And then copy over node_modules, etc from that stage to the smaller base image
FROM mhart/alpine-node:base
WORKDIR /app
COPY --from=builder /app/public ./public
COPY --from=builder /app/endpoint.js  ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.next ./.next
EXPOSE 3000
CMD ["sh","-c","node ./endpoint.js && node_modules/.bin/next start"]

But as you can see, i did a lot to solve a small question.
Does anyone have a convenient solution like 'process.env.ENDPOINT'?

This is also a problem with Azure App Service when you are using slots. You are not able to use slot swap when env variables are only resolved at build time. There should be an options to resolve env variables at start without disabling static optimizations.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

timneutkens picture timneutkens  路  3Comments

knipferrc picture knipferrc  路  3Comments

sospedra picture sospedra  路  3Comments

formula349 picture formula349  路  3Comments

havefive picture havefive  路  3Comments