nitro
nitro copied to clipboard
Support cloudflare environment variables
Reference docs: https://developers.cloudflare.com/workers/platform/environment-variables/
Related: https://github.com/nuxt/framework/issues/5056
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.
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.
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: https://github.com/timhanlon/nitro/commit/fabba1fefb8cba24fb49b9b104954c7b6d088d69
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.
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.
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
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
)
With CF worker format, we have another limitation that ENV is only accessible within a request. For this we might introduce
useRuntimeConfig?(event)
(also subsequentlyevent.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
@Hebilicious fix is needed (please merge his pr)
When will this be fixed, any ideas? @pi0
When will this be fixed, any ideas? @pi0
There's a PR with a fix #1318
When will this be fixed, any ideas? @pi0
There's a PR with a fix #1318
Please merge itπ
Hi, are there any updates concerning this issue? Thanks and kind regards, Davide
@DidoMarchet you can use process.env.MY_ENV
directly inside your server routes and it should works
With CF worker format, we have another limitation that ENV is only accessible within a request. For this we might introduce
useRuntimeConfig?(event)
(also subsequentlyevent.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
asruntimeConfig.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 https://github.com/unjs/unenv/pull/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 ?
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?
@beaudryj this will work, see working example here : https://github.com/Hebilicious/nuxt-authjs-google
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
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
// 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',
},
// 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.
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.
I have values set on both Production and Preview
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.
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
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>
@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.
@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