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

Support cloudflare environment variables #272

Closed
pi0 opened this issue Jun 8, 2022 · 24 comments · Fixed by #1547
Closed

Support cloudflare environment variables #272

pi0 opened this issue Jun 8, 2022 · 24 comments · Fixed by #1547
Labels
enhancement New feature or request preset:cloudflare

Comments

@pi0
Copy link
Member

pi0 commented Jun 8, 2022

Reference docs: https://developers.cloudflare.com/workers/platform/environment-variables/

Related: nuxt/nuxt#14011

Normally, we allow extending runtime config using Node.js environment variables (process.env.FOO) while cloudflare exposes env as global constant variables (accessible as FOO or globalThis.FOO).

We can add support for globalThis but since without prefix can be dangerous, I propose to support ENV_* variables for globalThis support. (Setting ENV_FOO in Cloudflare is same as FOO=x node when using Node.js) but the downside is makes usage harder and needing more docs. We might only enable prefixless support for Cloudflare.

@marshallswain
Copy link
Contributor

I'm not sure if it will change anything for this issue, but the new module workers receive their environment variables differently. They are no longer bound to the global scope and are instead passed as the second argument to the fetch handler. I've been keeping notes on how to add support in this discussion. It might not change anything, but it's also noteworthy that any change here might be very short-lived as the new esm-based modules format is required in order to use any of the new features of Cloudflare Workers like D1 (SQL Database) and Durable Objects.

@timhanlon
Copy link
Contributor

Since D1 hit public alpha, I've been trying to get something happening based on @marshallswain's discussion.

This WIP ESM preset is working, but as discussed, needs a way to access the environment to use service bindings, D1 etc: timhanlon@fabba1f

I'm keen to help push this forward, but unsure of an approach that would work for Nitro, given Cloudflare is one of many supported providers.

@chrisspiegl
Copy link

chrisspiegl commented Mar 23, 2023

This looks really promising with the ESM worker. I wonder if this can also be ported over to the Cloudflare Pages version too?

These types of things are going to be a game changer to get for example vuefire-nuxt working (eventually and maybe 🙈).

I also ran into the situation with the process.env not rendering in the frontend but being needed by other plugins / etc.

I think a workaround with namespaced cloudflare env => process.env. mapping would be great.

I quickly tested it locally with a patched version of nitropack and it did exactly what it needed to do.

@atinux
Copy link
Collaborator

atinux commented Apr 10, 2023

I like the idea of binding to process.env as well so it can stay consistent as in development.

But we need to find also a way to make it work for runtimeConfig

@pi0
Copy link
Member Author

pi0 commented Apr 10, 2023

With CF worker format, we have another limitation that ENV is only accessible within a request. For this we might introduce useRuntimeConfig?(event) (also subsequently event.context.env)

@Hebilicious
Copy link
Contributor

Hebilicious commented Jun 16, 2023

With CF worker format, we have another limitation that ENV is only accessible within a request. For this we might introduce useRuntimeConfig?(event) (also subsequently event.context.env)

I'm hitting this issue now and it's really inconvenient ...
For now I'm working around this issue by modifying my runtime code to do this :

  let apiKey = useRuntimeConfig().openaiApiKey as string
  if (apiKey.length === 0) {
    apiKey = event.context.cloudflare.env.NUXT_OPENAI_API_KEY
  }

For the module syntax and pages, we could unblock this quite easily by doing something like this in the Nitro runtime cloudflare presets :

setRuntimeConfig(env)

setRuntimeConfig would proxy the variables like env.NUXT_MY_VARIABLE as runtimeConfig.myVariable (we could also proxy NITRO_* by convention, or everything ?)

This doesn't work with service workers syntax, but we should deprecate it anyways, pretty sure there's no need to maintain both syntaxes in Nitro

Then forwarding the request to Nitro where user code would work as expected with useRuntimeConfig.

Another things that is cloudflare specific that we could do is copy the user .env file into a .dev.vars as it's needed for wrangler to load the environment variables locally.

That way everything would work both in dev and while testing the build locally.
Then for deploy the variables needs to bet set manually in the dashboard or with wrangler secrets put (workers only, doesn't work with pages), so we could display a message to the user that they should do that.

I can open a PR with this change @pi0

@kanelsnurren
Copy link

kanelsnurren commented Jul 22, 2023

@Hebilicious fix is needed (please merge his pr)

@kanelsnurren
Copy link

When will this be fixed, any ideas? @pi0

@Hebilicious
Copy link
Contributor

When will this be fixed, any ideas? @pi0

There's a PR with a fix #1318

@kanelsnurren
Copy link

When will this be fixed, any ideas? @pi0

There's a PR with a fix #1318

Please merge it🙌

@DidoMarchet
Copy link

Hi, are there any updates concerning this issue?
Thanks and kind regards,
Davide

Copy link
Collaborator

atinux commented Aug 4, 2023

@DidoMarchet you can use process.env.MY_ENV directly inside your server routes and it should works

@Hebilicious
Copy link
Contributor

Hebilicious commented Aug 7, 2023

With CF worker format, we have another limitation that ENV is only accessible within a request. For this we might introduce useRuntimeConfig?(event) (also subsequently event.context.env)

I'm hitting this issue now and it's really inconvenient ... For now I'm working around this issue by modifying my runtime code to do this :

  let apiKey = useRuntimeConfig().openaiApiKey as string
  if (apiKey.length === 0) {
    apiKey = event.context.cloudflare.env.NUXT_OPENAI_API_KEY
  }

For the module syntax and pages, we could unblock this quite easily by doing something like this in the Nitro runtime cloudflare presets :

setRuntimeConfig(env)

setRuntimeConfig would proxy the variables like env.NUXT_MY_VARIABLE as runtimeConfig.myVariable (we could also proxy NITRO_* by convention, or everything ?)

This doesn't work with service workers syntax, but we should deprecate it anyways, pretty sure there's no need to maintain both syntaxes in Nitro

Then forwarding the request to Nitro where user code would work as expected with useRuntimeConfig.

Another things that is cloudflare specific that we could do is copy the user .env file into a .dev.vars as it's needed for wrangler to load the environment variables locally.

That way everything would work both in dev and while testing the build locally. Then for deploy the variables needs to bet set manually in the dashboard or with wrangler secrets put (workers only, doesn't work with pages), so we could display a message to the user that they should do that.

I can open a PR with this change @pi0

Edit : After running further tests, it turns out that useRuntimeConfig(event) already works with cloudflare, cloudflare_module and cloudflare_pages thanks to unenv unjs/unenv#95 : useRuntimeConfig(event) will fallback to values in the global scope which will work consistently with nitro dev and nitro build. However this won't work without unenv, therefore I updated #1318 to reflect that. I will also make a separated PR to update the docs, and I believe this issue can be closed.

NITRO_OPENAI_API_KEY=abc #useRuntimeConfig(event).openaiApiKey will work with nuxt and nitro standalone
NUXT_OPENAI_API_KEY=abc #useRuntimeConfig(event).openaiApiKey will work with nuxt 
OPENAI_API_KEY=abc #useRuntimeConfig(event).openaiApiKey will not work

@lukamo1996 @DidoMarchet could you confirm that useRuntimeConfig(event) works for you ?

@beaudryj
Copy link

so.. if I am using

    sanity: {
      token: process.env.SANITY_TOKEN,
    },
    auth0: {
      clientID: process.env.Auth0__ClientId,
      redirectUrl: process.env.Auth0__RedirectUri,
      audience: process.env.Auth0__Audience,
      domain: process.env.Auth0__Domain,
      logoutReturnTo: process.env.Auth0__LogoutReturn,
    },
    public: {
      apiUrl: process.env.API_URL || 'https://localhost:5001',
      siteUrl: process.env.NUXT_PUBLIC_SITE_URL || 'http://localhost:4500',
      featureFlags: {
        mapFilters: process.env.FEATURE_MAPFILTERS === 'true',
      },
      googleAnalytics: {
        id: process.env.GOOGLE_ANALYTICS_ID,
        enabled: false,
      },
    },
  },

what do I do for cases that I do not have an event, or how does this work out for the client?

@Hebilicious
Copy link
Contributor

@beaudryj this will work, see working example here : https://github.com/Hebilicious/nuxt-authjs-google

@beaudryj
Copy link

I had that, but it does not seem to work. I want to note that i do my preset build in my CI and ship my dist directory to cloudflare. If i was building on cloudflare do the envvars get set on their wokers at a build time? Or does nitro set the values there on page request

@Hebilicious
Copy link
Contributor

I had that, but it does not seem to work. I want to note that i do my preset build in my CI and ship my dist directory to cloudflare. If i was building on cloudflare do the envvars get set on their wokers at a build time? Or does nitro set the values there on page request

You need to manually set the variables, you can use wrangler or the dashboard, see instructions in PR here
https://github.com/unjs/nitro/pull/1547/files

@beaudryj
Copy link

beaudryj commented Aug 15, 2023

// In development
export default defineEventHandler((event) => {
  useRuntimeConfig(event).helloThere //general
  useRuntimeConfig(event).secret //undefined
  // Module syntax (cloudflare_module, cloudflare_pages)
  event.context.cloudflare.env.NITRO_HELLO_THERE //general
  event.context.cloudflare.env.SECRET //secret
  // Service worker syntax (cloudflare)
  NITRO_HELLO_THERE //general
  SECRET //secret
});

this block you have here though would not work for pages without events? what do I do in those scenarios

or is it more my nuxt.config.ts should be setup as

      apiUrl: event.context.cloudflare.env.API_URL || 'https://localhost:5001',
      siteUrl: event.context.cloudflare.env.NUXT_PUBLIC_SITE_URL || 'http://localhost:4500',
      showFeedback: event.context.cloudflare.env.SHOW_FEEDBACK === 'true',
      logRocketId: event.context.cloudflare.env.LOGROCKET_ID,
      pubnubPublishKey:event.context.cloudflare.env.PUBNUB_PUBLISH_KEY,
      pubnubSubscribeKey: event.context.cloudflare.env.PUBNUB_SUBSCRIBE_KEY,
      featureFlags: {
        mapFilters: event.context.cloudflare.env.FEATURE_MAPFILTERS === 'true',
      },

@Hebilicious
Copy link
Contributor

// In development
export default defineEventHandler((event) => {
  useRuntimeConfig(event).helloThere //general
  useRuntimeConfig(event).secret //undefined
  // Module syntax (cloudflare_module, cloudflare_pages)
  event.context.cloudflare.env.NITRO_HELLO_THERE //general
  event.context.cloudflare.env.SECRET //secret
  // Service worker syntax (cloudflare)
  NITRO_HELLO_THERE //general
  SECRET //secret
});

this block you have here though would not work for pages without events? what do I do in those scenarios

or is it more my nuxt.config.ts should be setup as

      apiUrl: event.context.cloudflare.env.API_URL || 'https://localhost:5001',
      siteUrl: event.context.cloudflare.env.NUXT_PUBLIC_SITE_URL || 'http://localhost:4500',
      showFeedback: event.context.cloudflare.env.SHOW_FEEDBACK === 'true',
      logRocketId: event.context.cloudflare.env.LOGROCKET_ID,
      pubnubPublishKey:event.context.cloudflare.env.PUBNUB_PUBLISH_KEY,
      pubnubSubscribeKey: event.context.cloudflare.env.PUBNUB_SUBSCRIBE_KEY,
      featureFlags: {
        mapFilters: event.context.cloudflare.env.FEATURE_MAPFILTERS === 'true',
      },

You don't need to do that,process.env will work thanks to unenv. However, you have to set the env variables with wrangler or the cf dashboard.

@beaudryj
Copy link

beaudryj commented Aug 15, 2023

I have them set in the dashboard. Build my projects with terraform and they are setup on the project I am deploying to. And I am saying that process.env in my runtimeConfig is not working. I deploy my latest to my configured project and the cloudflare variables are not being pulled.
image

I have values set on both Production and Preview

@Hebilicious
Copy link
Contributor

I have them set in the dashboard. Build my projects with terraform and they are setup on the project I am deploying to. And I am saying that process.env in my runtimeConfig is not working. I deploy my latest to my configured project and the cloudflare variables are not being pulled. image

I have values set on both Production and Preview

Hard to tell more without a reproduction, but I think your variables need to be prefixed by NUXT_ or NITRO_ in your nitro.config.ts/ nuxt.config.ts

https://nuxt.com/docs/guide/going-further/runtime-config#environment-variables
https://nitro.unjs.io/guide/configuration#update-runtime-config-using-environment-variables

@beaudryj
Copy link

beaudryj commented Aug 16, 2023

image
neither made a difference.

in my

server route sample:

server/routes/signin.js

import { createNonce } from '~/utils/auth';
import buildUrl from 'build-url';
import { defineEventHandler, setCookie } from 'h3';

export default defineEventHandler(async (event) => {
  const runtimeConfig = useRuntimeConfig(event);
  const nonce = createNonce();
  setCookie(event, 'nonce', nonce, { secure: true });

  const url = buildUrl(`https://${runtimeConfig.auth0.domain}`, {
    path: 'authorize',
    queryParams: {
      client_id: runtimeConfig.auth0.clientID,
      response_type: '',
      redirect_uri: runtimeConfig.auth0.redirectUrl,
      scope: '',
      audience: runtimeConfig.auth0.audience,
      nonce: nonce,
    },
  });

  return sendRedirect(event, url, 302);
});

app.vue sample

<template>
  <NuxtLayout>
    <NuxtLoadingIndicator />
    <NuxtPage />
  </NuxtLayout>
</template>

<script setup lang="ts">
const route = useRoute();
const runtimeConfig = useRuntimeConfig();
const domain = runtimeConfig.public.domain;

useHead({
  titleTemplate: '%s - Leagued',
  viewport: 'width=device-width, initial-scale=1',
  charset: 'utf-8',
  htmlAttrs: {
    lang: 'en',
  },
  link: [
    {
      rel: 'apple-touch-icon',
      sizes: '180x180',
      href: '/favicon/apple-touch-icon.png',
    },
    {
      rel: 'icon',
      type: 'image/png',
      sizes: '32x32',
      href: '/favicon/favicon-32x32.png',
    },
    {
      rel: 'manifest',
      type: 'image/png',
      sizes: '16x16',
      href: '/favicon/favicon-16x16.png',
    },
    { rel: 'manifest', href: '/favicon/site.webmanifest' },
    {
      rel: 'mask-icon',
      color: '#5bbad5',
      href: '/favicon/safari-pinned-tab.svg',
    },
    { rel: 'shortcut icon', href: '/favicon/favicon.ico' },
    { name: 'msapplication-TileColor', content: '#ffc40d' },
    { name: 'msapplication-config', href: '/favicon/browserconfig.xml' },
    { name: 'theme-color', content: '#ffffff' },
    {
      rel: 'canonical',
      href: () => `https://example.com${route.path}`,
    },
  ],
});
</script>

@Hebilicious
Copy link
Contributor

@beaudryj If you are confident that there's a bug, I recommend that you open an issue in the Nuxt repository with a minimal reproduction attached.

@beaudryj
Copy link

@Hebilicious thank you for the back and fourth on this.

I found what was tripping us up - https://v2.nuxt.com/docs/configuration-glossary/configuration-env/#processenv--

further digging into the docs I noticed this -

export default {
 runtimeConfig: {
    apiKey: '' // Default to an empty string, automatically set at runtime using process.env.NUXT_API_KEY
    public: {
       baseURL: '' // Exposed to the frontend as well.
    }
  }
}

So I did a deployment using Default to an empty string, automatically set at runtime using process.env.NUXT_API_KEY and it seems to have resolved

@pi0 pi0 closed this as completed in #1547 Aug 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request preset:cloudflare
Projects
None yet
9 participants