-
-
Notifications
You must be signed in to change notification settings - Fork 86
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
Validating environment variables with Astro Server-side Rendering #60
Comments
Thank you for the write-up. I had to read it 3 times, before I understood it, and most things work. However in Cloudflare Workers, I do have issues setting up server-side runtime vars. The
|
Weird, since the Astro build step has nothing to do with Cloudflare Workers. Can you run the Astro build script locally and confirm that the |
My bad explaining. I'm running the local build step So I made everything work, except server-side runtime vars. Not sure what it takes to make that work with Cloudflare Pages, the Input //t3-env.ts
const serverValidation = {
build: {
// Put in here any server-side variable that is hardcoded on build, i.e.
// defined with Vite config's `define` option.
TIME: z.string().nonempty(),
},
runtime: {
// Put in here any server-side variable that is read at runtime, i.e. being
// read from `process.env`.
CF_PAGES_URL: z.string().nonempty(),
},
}
export const env = createEnv({
server: (isBuilding
? serverValidation.build
: {
...serverValidation.build,
...serverValidation.runtime,
}) as typeof serverValidation.build & typeof serverValidation.runtime,
clientPrefix: "PUBLIC_",
client: {
PUBLIC_API_URL: z.string(),
},
// We can't use vite's import meta env here, because it's not loaded yet
runtimeEnvStrict: {
TIME: isServer ? process.env.TIME : undefined,
CF_PAGES_URL: isServer ? process.env.CF_PAGES_URL ?? devEnv("CF_PAGES_URL") : undefined,
PUBLIC_API_URL: isBuilding ? process.env.PUBLIC_API_URL : import.meta.env.PUBLIC_API_URL ?? fileEnv.PUBLIC_API_URL,
},
skipValidation:
!!process.env.SKIP_ENV_VALIDATION &&
process.env.SKIP_ENV_VALIDATION !== "false" &&
process.env.SKIP_ENV_VALIDATION !== "0",
}) Output //_workers.js
var env = g({
server: isBuilding ? serverValidation.build : {
...serverValidation.build,
...serverValidation.runtime
},
clientPrefix: "PUBLIC_",
client: {
PUBLIC_API_URL: z.string()
},
// We can't use vite's import meta env here, because it's not loaded yet
runtimeEnvStrict: {
TIME: isServer ? "1234" : void 0,
CF_PAGES_URL: isServer ? {}.CF_PAGES_URL ?? devEnv("CF_PAGES_URL") : void 0,
PUBLIC_API_URL: isBuilding ? {}.PUBLIC_API_URL : "http://localhost:8788/trpc"
},
skipValidation: !!{}.SKIP_ENV_VALIDATION && {}.SKIP_ENV_VALIDATION !== "false" && {}.SKIP_ENV_VALIDATION !== "0"
}); |
@alexanderniebuhr If I'm not mistaken, Found this in |
Kinda you are right (and that might be the issue here), because the runtime variables are set in Cloudflare after the build is finished. So really astro has nothing to do with it, and can't know them at build-time. If replaced with If I replace the |
Actually I don't see any Cloudflare's runtime environment variables are not in What I suspect is that you kinda have to pass |
That is correct, however not sure if that is possible & if that would work on initial start before a request is send.
Actually they are accessible by See this example, sadly |
Yes, all of that is correct. Though it is certainly possible. You are given a little space on a Worker to perform tasks, which means each of your request has a different environment, which means you have to run the validation per request i.e. at the start of each individual request, not at initial start. There's no initial start in Cloudflare Workers, the workers are not your app's, it is shared between possibly thousands of apps. Move |
This comment was marked as outdated.
This comment was marked as outdated.
I personally thing Astro support t3-env is far from ideal, IMO following is still missing (to be edited):
UPDATE // t3-env.ts (the file you use for validation)
import { createEnv } from "@t3-oss/env-core"
import { z } from "zod"
const isBuild = process?.env?.STATE === "building"
const serverVariables = {
build: {
// Put in here any server-side variable that is hardcoded on build
VITE_TIME: z.string().nonempty(),
},
runtime: {
// Put in here any server-side variable that is read at runtime
CF_PAGES_URL: z.string().nonempty(),
},
}
export const createRuntimeEnv = (prop?: unknown) => {
const rEnv = prop as Record<string, string | number | boolean | undefined>
return createEnv({
skipValidation:
!!process.env.SKIP_ENV_VALIDATION &&
process.env.SKIP_ENV_VALIDATION !== "false" &&
process.env.SKIP_ENV_VALIDATION !== "0",
server: isBuild ? serverVariables.build : { ...serverVariables.runtime, ...serverVariables.build },
clientPrefix: "PUBLIC_",
client: {
PUBLIC_API_URL: z.string(),
},
// check if rEnv is empty, if so use only import.meta.env, otherwise combine with env from runtime / build
runtimeEnv: rEnv ? { ...rEnv, ...import.meta.env } : import.meta.env,
})
} // astro.config.ts
import { loadEnv } from "vite"
try {
await import("./src/t3-env").then((m) => m.createRuntimeEnv(loadEnv(import.meta.env.MODE, process.cwd(), "")))
} catch (error) {
console.error(error)
process.exit(1)
}
// https://astro.build/config
export default defineConfig({
vite: {
define: {
"process.env.VITE_TIME": JSON.stringify(process.env.VITE_TIME),
},
},
}) |
I updated the original issue with my new setup. There were some misunderstandings of the Vite build step, so I documented the correct behavior in the code. As for @alexanderniebuhr's solution:
|
Agree! There are different solutions for different adapters, and your new one looks great for Node runtimes. We should maybe consider splitting this issue then :) |
Note: The following is kind of like a recipe page awaiting its debut in the t3-env docs. Please consider this a proposal before I (or someone else) do so.
When Astro is built with the default Static Site Generation (SSG), the validation will happen on each static route generation at the place of
env
usage. Technically this is "Validation on build".However, with Astro Server-side Rendering (SSR), validation takes a different "route" (no pun intended). If we just switch from SSG to SSR with
npm astro add node
for example, immediately we will see that no validation is run when runningnpm run build
. Let's force it by importing the validation file inastro.config.ts
:Now
npm run build
will tell us that our environment variables are nowhere to be found inimport.meta.env
. How comes?With Astro SSR, you have 1 chance at running the validation at build time, which is the moment when
astro.config.ts
is evaluated at the beginning of the build. Unfortunatelyimport.meta.env
at this "config reading time" is not populated yet, so you have to fallback toprocess.env
or reading the.env
files manually with Vite'sloadEnv
function. Thus puttingimport.meta.env
intoruntimeEnv
doesn't work anymore with Astro SSR.When working with Astro SSR, because there are the build-time and the runtime, there exists different types of environment variables. Here is one way to do it in Astro SSR, Node.js runtime. Note that each runtime has different ways of configuring t3-env, I'm just providing one of the Node runtime solutions.
The text was updated successfully, but these errors were encountered: