T
TanStack2mo ago
xenophobic-harlequin

Environment Variables Issue with TanStack Start v1.129.7

Hi! I deployed a new TanStack Start app (v1.129.7) and noticed it no longer uses Vinxi. I'm having problems accessing environment variables in server-side code during pnpm run dev. Can anyone point me to resources for solving or diagnosing this issue? I've already checked the Vite env guide, but nothing seems amiss. Sample Code
export const getRecords = createServerFn({
method: "GET",
response: "data",
type: "dynamic"
})
.validator(
z.object({
period: z.enum(["hour", "day", "week", "month", "year", "all_time"]),
}),
)
.handler(async ({ data }) => {
console.log("SERVER", import.meta.env.VITE_APP_URL);
console.log("SERVER", import.meta.env.POCKETBASE_URL);
console.log("SERVER", process.env.POCKETBASE_URL);
console.log("SERVER", import.meta.env.MODE);
// ...
});
export const getRecords = createServerFn({
method: "GET",
response: "data",
type: "dynamic"
})
.validator(
z.object({
period: z.enum(["hour", "day", "week", "month", "year", "all_time"]),
}),
)
.handler(async ({ data }) => {
console.log("SERVER", import.meta.env.VITE_APP_URL);
console.log("SERVER", import.meta.env.POCKETBASE_URL);
console.log("SERVER", process.env.POCKETBASE_URL);
console.log("SERVER", import.meta.env.MODE);
// ...
});
Issue During pnpm run dev: - Variables are defined when using process.env - Variables are undefined when using import.meta.env My theory: createServerFn might be treated as client-side code since the test env VITE_APP_URL is defined. Is there a way to check if this is the case? Note: Same behavior occurs with pnpm run build && pnpm run start Configuration vite.config.ts:
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import tsConfigPaths from "vite-tsconfig-paths";

export default defineConfig({
server: {
port: 3000,
},
plugins: [
tsConfigPaths({
projects: ["./tsconfig.json"],
}),
tanstackStart({ customViteReactPlugin: true }),
viteReact(),
],
envPrefix: "VITE_",
});
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import tsConfigPaths from "vite-tsconfig-paths";

export default defineConfig({
server: {
port: 3000,
},
plugins: [
tsConfigPaths({
projects: ["./tsconfig.json"],
}),
tanstackStart({ customViteReactPlugin: true }),
viteReact(),
],
envPrefix: "VITE_",
});
package.json (relevant parts):
{
"scripts": {
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
"start": "node .output/server/index.mjs"
},
"dependencies": {
"@tanstack/react-start": "^1.129.7",
// ... other deps
}
}
{
"scripts": {
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
"start": "node .output/server/index.mjs"
},
"dependencies": {
"@tanstack/react-start": "^1.129.7",
// ... other deps
}
}
.env sample
POCKETBASE_URL=http://localhost:8090
POCKETBASE_EMAIL=example@exampe.com
POCKETBASE_PASSWORD=password123
VITE_APP_URL=http://example.com
POCKETBASE_URL=http://localhost:8090
POCKETBASE_EMAIL=example@exampe.com
POCKETBASE_PASSWORD=password123
VITE_APP_URL=http://example.com
Any insights would be greatly appreciated! 🙏
25 Replies
xenophobic-harlequin
xenophobic-harlequinOP2mo ago
this is the console output for the server-side sample code
No description
xenophobic-harlequin
xenophobic-harlequinOP2mo ago
I got it working by using loadEnv in my vite.config.ts and just using process.env for env variables in server-side code https://stackoverflow.com/questions/70709987/how-to-load-environment-variables-from-env-file-using-vite is this the recommended approach? still unsure why it wasn't working at first
stormy-gold
stormy-gold2mo ago
this is what i have been doing for a long time yes
stormy-gold
stormy-gold2mo ago
No description
evident-indigo
evident-indigo2mo ago
You mentioned the VITE_APP_URL worked. With vite you have to prefix your variable names in .env with VITE_ . check the vite docx
stormy-gold
stormy-gold2mo ago
do beware that VITE_ env are basically compile time macros though you can't expect them to be read at runtime they're all replaced by the value from the env at compile time compile time as in bundle time
xenophobic-harlequin
xenophobic-harlequinOP2mo ago
my understanding is that these would also be exposed to the client side, which was not something I intended to do for security purposes
stormy-gold
stormy-gold2mo ago
exactly yes
extended-salmon
extended-salmon2mo ago
hey folks, I'm a bit lost here 😅 i'm using cloudflare to deploy tanstack start import.meta.env vars prefixed with VITE are available during SSR and in the client in dev, but are gone in prod process env vars without prefix are availabe during SSR in both dev and prod but are gone in client (probably because missing VITE prefix) but VITE prefixed env vars are not available in process.env i'm just really confused what how I should handle env vars with cloudflare 😅 any idea what the best and safest solution is without me accidently leaking secrets?
stormy-gold
stormy-gold2mo ago
1. where does the build occur ? 2. did you set the env where the build occurs ? 3. are you aware that VITE env vars are NOT runtime env vars, but bundle/build time vars ? If you really want runtime client env vars, then you will have to have non VITE prefixed vars, and get them via requests (server functions for example)
fair-rose
fair-rose2mo ago
@notKamui want to contribute docs about environment handling in start? build time vs runtime, client vs server, how to validate etc?
stormy-gold
stormy-gold2mo ago
with the amount of time this has been brought up and i answered, i probably will haha
fair-rose
fair-rose5w ago
looks like we recently got some docs https://github.com/TanStack/router/commit/4f802cf1c0cab97a86cc3f3d4f18821ae0b6bf2e can you please check whether this aligns with your experience?
GitHub
docs: env vars (#4777) · TanStack/router@4f802cf
Add a detailed plan for a new 'How to Use Environment Variables' guide because it addresses a critical and frequently asked topic that is currently under-documented. --- [Open in ...
stormy-gold
stormy-gold5w ago
sure i think it’s pretty good, but to avoid more questions i would emphasize in the « issues » sections that VITE_ will never be read at runtime, because this seems to be THE most common issues people have. I know Tanner mentioned it, but i feel this could be more detailed and explicit, with examples and situations Like, he mentions getting runtime env from server side, but no examples provided and that’s only mentioned in the router docs, not start even there’s a pretty « default » way of doing it with sever functions
fair-rose
fair-rose5w ago
can you add this via a PR?
stormy-gold
stormy-gold5w ago
not right now but probably tomorrow yes
fair-rose
fair-rose5w ago
no hurry
extended-salmon
extended-salmon5w ago
thank you for your time 🙏 1. it's build in cloudflare 2. yes, all env vars are set in cloudflare 3. meaning they will be inlined during build, correct? Yes, I'm aware of that the confusion I have is process.env vs import.meta.env, because apparently they are not inlined during build? let me double check this though, I might have fiddled around too much. my question though is, how do should I use env vars? should I use process.env in server functions and import.meta.env client side? could you maybe show me your /src/lib/env set up? I'm assuming you're using something like zod or t3-oss/env? Also, happy to help with the docs once I also figured it out 😅
stormy-gold
stormy-gold5w ago
GitHub
GitHub - notKamui/miniverso: Self-hostable grouping of mini web app...
Self-hostable grouping of mini web applications for everyday use - notKamui/miniverso
stormy-gold
stormy-gold5w ago
yes process.env on the server and import.meta.env on the client i’m using t3 env do check out the vite config though this is where i explicitly load the env for validation
extended-salmon
extended-salmon5w ago
gotcha, I have the feeling that my issue is a cloudflare deployment issue, I just double checked and import.meta.env is not available after deployment thanks for sharing the code 🙏
constant-blue
constant-blue5w ago
I deploy from a docker image which has configurable env vars. This probably similar to what you want? I essentially use the root loader to get the vars using process.env then I hydrate client state with them So it gets the vars on initial SSR load, then you just have to store those results. I don't know how you're storing client state, so it would depend. I'm using jotai and stroing the results are as simple as:
const { env } = Route.useLoaderData();
useHydrateAtoms([[envAtom, env]]);
const { env } = Route.useLoaderData();
useHydrateAtoms([[envAtom, env]]);
With the loader:
loader: async () => {
const env = getEnvVars();
return { env };
}
loader: async () => {
const env = getEnvVars();
return { env };
}
The getEnvVars just returning some keys from process.env You could also check if import.meta.env.SSR is true in the loader so that it won't try to get env vars on the client again (or potentially throw error while it tries to mes with process.env)
extended-salmon
extended-salmon5w ago
thanks for the snippet, the solution makes sense I feel like there must be a way to still inline VITE env vars though? the workaround could potentially make it easier to accidentally leak secrets well i'm just really stupid... the env vars were indeed not available during build, in cloudflare there's a different section for build env vars, now everything works as expected. 🤦 thanks everyone for the help 😅
constant-blue
constant-blue5w ago
Inline VITE_ vars won't allow you to change env vars after the build step, which is what I needed. I needed to build it just once but still change things with env vars. But if you build it on deploy with known env vars, than I'd use them.
stormy-gold
stormy-gold5w ago
great, glad it worked for you

Did you find this page helpful?