How to pass env variables to nuxt in production?
Asked Answered
S

10

15

nuxt.config.js

modules: [
    '@nuxtjs/dotenv'
  ],

server/index.js

const express = require('express')
const consola = require('consola')
const { Nuxt, Builder } = require('nuxt')
const app = express()
const host = process.env.HOST || '0.0.0.0'
const port = 8080
app.set('port', port)
// Import and Set Nuxt.js options
let config = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')

const Storage = require('@google-cloud/storage')
const dotenv = require('dotenv')


async function getEnv() {
  if (config.dev) {
    dotenv.config({ path: '.env' })
    console.log('Environment local .env file loaded.')
    console.log(process.env.LOCALE)

    return
  }

  try {

    const bucketName = 'env-var'

    const dotEnvSourcePath = `.env`
    const dotEnvDestinationPath = `/tmp/${dotEnvSourcePath}`
    const storage = new Storage({})

    await storage
      .bucket(bucketName)
      .file(dotEnvSourcePath)

      .download({ destination: dotEnvDestinationPath })
    console.log(
      `gs://${bucketName}/${dotEnvSourcePath} downloaded to ${dotEnvDestinationPath}.`
    )


    dotenv.config({ path: dotEnvDestinationPath })


  } catch (err) {
    console.error('ERROR:', err)
  }
}

async function afterEnvProcess() {
  // Init Nuxt.js
  const nuxt = new Nuxt(config)

  // Build only in dev mode
  if (config.dev) {
    const builder = new Builder(nuxt)
    await builder.build()
  }

  // Give nuxt middleware to express
  app.use(nuxt.render)

  // Listen the server
  app.listen(port, host)
  consola.ready({
    message: `Server listening on http://${host}:${port}`,
    badge: true
  })
  const fs = require('fs')

  const dotEnvExists = fs.existsSync('.env')
}

getEnv()
  .then(r => afterEnvProcess())
  .catch(e => console.log(e))

I get the values for process.env.<variable> as undefined when running the app in production. When running in development, I get the values correctly. It seems the env variables are not getting passed to the nuxt env property.

EDIT 1: I can see the correct values in google cloud logs when I console log the env variables with process.env. but at the same time those console log statements show undefined in the browser console

Suzan answered 1/1, 2019 at 7:54 Comment(1)
consider selecting an answer :)Methylene
M
26

Most people use dotenv package but I dislike this solution, because it adds the need for managing an extra file different for production and development, while you can automate webpack to use the right values without extra hassle.

A simpler way :

//package.json
  "scripts": {
    "dev": "NODE_ENV=dev nuxt"
  }
//nuxt.config.js
  env: {
    baseUrl:
      process.env.NODE_ENV === 'dev'
        ? 'http://localhost:3000'
        : 'https://my-domain.com'
  }

This will allow you to use the right value by calling process.env.baseUrl. Note that you can verify this with console.log(process.env.baseUrl) but not console.log(process.env), at least in Chrome.

Methylene answered 11/5, 2019 at 6:34 Comment(3)
Just as a note, you don't need to have a .env file on other environments using dotenv, as it picks up the environment variables if they are there already.Maihem
best solution. But please note that you don't need === 'dev'per each variable. Use this instead to save space: env: process.env.NODE_ENV === 'dev' ? { A: "1", B: "2" } : { A: "3", B: "4" },Julienne
good hint! Had to add a .env cerate in my build pipelines and now it worksUnarmed
F
15

Most updated answer (Nuxt 3).

  1. Do not use @nuxtjs/dotenv, replacement is implemented in framework from Nuxt v2.13
  2. Use env names starting from NUXT for private and NUXT_PUBLIC if can be exposed for browser

Example of your .env (in file locally and as environment variables on production)

NUXT_API_SECRET=api_secret_token
NUXT_PUBLIC_API_BASE=https://nuxtjs.org

Example of nuxt.config.ts

export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: process.env.NUXT_API_SECRET ?? 'default',
    public: {
      apiBase: process.env.NUXT_PUBLIC_API_BASE ?? 'default value',
    }
  },
})

and you can use it in components

<script setup>
const config = useRuntimeConfig()
console.log('Runtime config:', config)
if (process.server) {
  console.log('API secret:', config.apiSecret)
}
</script>

or plugins

export default defineNuxtPlugin((nuxtApp) =>
  const config = useRuntimeConfig()
  console.log('API base URL:', config.public.apiBase)

in server directory you will still have access to process.env directly.

I know that there already 10 other answers, but feel that all of these are incomplete, because following this guide you are able to inject variable after build phase (on runtime) what is good practice described in "12 factor".

In some case you can be interested in "app config" instead of "runtime config". Below link to comparison:

https://nuxt.com/docs/getting-started/configuration#runtimeconfig-vs-appconfig

Sources:

Floodgate answered 16/11, 2022 at 15:30 Comment(6)
Thanks for the complete answer, no issue giving more context/details/links in my opinion!Iceberg
Can you add a new environment variable based on another environment variable? It doesn't seem to work in production... It was possible in Nuxt 2 but not Nuxt 3 (in my case nuxt-bridge). js runtimeConfig: { public: { USER_PROFILE_API: "", // works locally and on deployment using NUXT_PUBLIC_USER_PROFILE USER_PROFILE_POSTS_API: process.env.NUXT_PUBLIC_USER_PROFILE_API+"/posts", // this doesn't work on production, but locally with .env file ... } } Enceinte
oemera I did not experienced this problem deploying this. But note, that using this method you have to give your production process access to variables. They are not injected during build time, but on runtime.Floodgate
While the above approach works for development, Production has a different expectation from the documentation.Pawnshop
You do not need to use process.env.NUXT_API_SECRET etc. in defineNuxtConfig. Just use default values. Nuxt will read the corresponding env variables (according to the naming convention) and set runtimeConfig values automatically.Oteliaotero
After your server is built, you are responsible for setting environment variables when you run the server. Your .env files will not be read at this point. How you do this is different for every environment. nuxt.com/docs/guide/directory-structure/env#productionLewan
L
14

nuxt.config.env Gotcha!

For future Googler's including myself, there's an annoying gotcha which nuxt.config.js doesn't explain very well.

process.env.SOMETHING is literally replaced during the build with the config.env.SOMETHING value

before build

    if (process.env.SOMETHING == 'testing123')
    //     ^     ^ this object path won't exist after the build

after build

    if ('testing123' == 'testing123')

This also doesn't work with objects! only literals.

// This won't work for you!
mounted() {
  console.log('process.env', process.env)
}

https://nuxtjs.org/api/configuration-env/

Larisa answered 27/11, 2019 at 5:42 Comment(1)
Don't understand please explain furtherWheelbase
E
5

Env variables bundled at build time. So you need to set them when you are building for production

They will be available at runtime in your server/index.js, but when nuxt build dist it replace process.env.* with the values that was passed at build time, so it dont really matter what u pass when u start server for this variables.

Extradite answered 1/1, 2019 at 11:40 Comment(5)
When I do a console.log, I can see the values correctly in google cloud logs but in browser console I see undefined. Could it mean that they are available in production? Sorry, I am new to this. If you could expand your answer, it would help. ThanksSuzan
Can you pass in environment vars when calling npm run generate? Trying to work out how to do this in Windows.Alie
For example, I have: .env and .env.debug, so how can I build a version with .env.debug?Hatchery
Agree with this comment, I could see a lot of answers but as overall, NuxtJS no yet the feature to pass environment variable at Production Runtime, its runtime config mean to use for Development Stage and Built Stage, so you need to have different built if you have to attach to different API link.Colous
That doesn't sound good! I am just facing this strange thing too. What about building in prod and storing in a docker image? What about security? I don't want to embed any potential secrets in there...Hypergolic
G
4

As @Aldarund said before: Environment variables are set at build time and not at runtime.

As of Nuxt.js 2.13+ you can use runtime config and built-in dotenv support. Which provides better security and faster development.

To use environment variables as axios baseUrl at runtime you can use:

publicRuntimeConfig: {
    axios: {
      baseURL: process.env.BASE_URL
    }
  },

More information about runtime config: https://nuxtjs.org/blog/moving-from-nuxtjs-dotenv-to-runtime-config/

More information about axios runtime config: https://axios.nuxtjs.org/options

Guan answered 11/9, 2020 at 11:27 Comment(0)
J
2

UPD. This will work only in universal mode, because nuxtServerInit will not called in SPA mode.

You can build Nuxt for production without any environment variables. And then set it in store in nuxtServerInit.

I use env-cmd for this.

I have .env-cmdrc file with next content:

{
  "production": {
    "API_URL": "https://api.example.com/",
    "IMG_URL": "https://img.example.com/",
    "ENV_PATH": "./.cmdrc.json"
  },
  "staging": {
    "API_URL": "https://stage.example.com/",
    "IMG_URL": "https://stage.img.shavuha.com/",
    "ENV_PATH": "./.cmdrc.json"
  },
  "development": {
    "API_URL": "https://stage.api.example.com/",
    "IMG_URL": "https://stage.img.example.com/",
    "ENV_PATH": "./.cmdrc.json"
  }
}

In store I have something like this:

export const state = () => ({
  api_url: '',
  img_url: ''
})

export const mutations = {
  SET_PROCESS_ENV: (state, payload) => {
    state.api_url = payload.api_url
    state.img_url = payload.img_url
  }
}

nuxtServerInit action:

  commit('settings/SET_PROCESS_ENV', {
    api_url: process.env.API_URL,
    img_url: process.env.IMG_URL
  })

package.json:

"dev": "env-cmd -e development -r .env-cmdrc nuxt",
"build": "nuxt build",
"start_stage": "env-cmd -e staging -r .env-cmdrc nuxt start",
Judoka answered 8/7, 2019 at 10:56 Comment(1)
This looks like very interesting aproach, it may be worth to mention it won't propably work when SPA mode is used - according to docs nuxtServerInit action is called only from the server-sideWiskind
M
1

I've created a function that can update module settings from the /server/index.js even in production.

This is only designed to work with the array-style module config syntax. Like this

['@nuxtjs/google-gtag', { ... }]

nuxt.config.js

// Import and Set Nuxt.js options
const config = require('../nuxt.config.js')
config.dev = process.env.NODE_ENV !== 'production'

function updateConfigModuleSettings(name, settings) {
  const i = config.modules.findIndex(m => Array.isArray(m) && m[0] === name)
  if (i > -1) {
    const oldSettings = config.modules[i][1]
    config.modules[i][1] = {
      ...oldSettings,
      ...settings
    }
  } else {
    throw new RangeError(`Nuxt module named '${name}' could not be found`)
  }
}

// call the function as many times as you like with your module settings overrides
updateConfigModuleSettings('@nuxtjs/google-gtag', {
  id: process.env.GOOGLE_ANALYTICS_ID,
  debug: process.env.NODE_ENV === 'development', // enable to track in dev mode
})

async function start () {
  // this will take the overridden config
  const nuxt = new Nuxt(config)

  // ...
}
start()
Mississippian answered 14/2, 2020 at 20:12 Comment(0)
C
1

It works this way!

I've been struggle for that a bit long and has been comment around the post since early 2021 but I have now triggered out to do it properly now.

Nuxt runtime environment to use as normal by setting them in block of publicRuntimeConfig, like this

publicRuntimeConfig: {
   myVariable: process.env.MY_VARIABLE || '',
   axios: {
      baseURL: process.env.BASE_URL || 'http://api.example.com',
    },
}

The most confuse point that we do, mostly we use this code in our block

if (process.env.MY_VARIABLE == 'something') {
}

The correct way, we need to use as this:

if (this.$config.myVariable == 'something') {
}

And for axios, it should be well treated by nuxtjs, except if you have your own rule to pass the baseUrl, at component side, you need to do it via this.$config call and do not use process.env.xxx

And now at each environment, I just have my own .env and passing the variable as we use in build time, everything works well, including via docker.

Colous answered 29/8, 2022 at 4:51 Comment(0)
Z
0

On Windows with Nuxt 3 RC11 it simply worked this way:

 if (process.env.NODE_ENV === "development") {
    return "http://localhost:8000";
  } else {
    return "https://my-domain.com";
  }

It seems that the NODE_ENV is set correctly when running npm run dev on production the NODE_ENV env variable is production.

Zendejas answered 1/10, 2022 at 7:6 Comment(0)
F
0

Environment variable config steps for Nuxt 3

Doing it this way ensures it works with both client and server side rendering.

  1. Create a .env file in the nuxt app root directory and define the value of the environment variable there.
MYENV="MyEnvironmentVariableValue"
  1. In the nuxt.config or nuxt.config.ts file, set the value in the runtimeConfig
export default defineNuxtConfig({
  ...,
  runtimeConfig: {
    public: {
      myenv: process.env.MYENV || 'myDefaultValue'
    },
  ...
})
  1. Access the environment variable in any component or page
<template>
  <div>
    <h1>{{myEnv}}</h1>
  </div>
</template>

<script setup lang="ts">
const config = useRuntimeConfig();
const myEnv = config.public.myenv;
</script>
  1. Restart the nuxt app (e.g. yarn dev)
Faletti answered 21/9, 2023 at 19:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.