T
TanStack3mo ago
plain-purple

Runtime client env variables

Our workflows rely on creating docker images that can be reused across different environments. Because of this I've been trying to add the ability to use runtime client environment variables rather than relying on import.meta.env to bake them into the image during build. I have been trying to implement it using https://import-meta-env.org/ but can't get a working solution, especially when I introduce t3/env. Anyone managed to achieve this or similar?
Import-meta-env | Startup/Runtime environment variables solution fo...
Build once, deploy anywhere. Import-meta-env helps to developing applications following the 12-factor principles.
4 Replies
deep-jade
deep-jade3mo ago
one thing you can do is load the env vars from a serverFn in __root's before load, and then make it available as root context or query data. If you write it as a react query with infinite stale time, it will only be fetched once, and since it's during the initial ssr it won't add overhead. i know you probably want something that just works with t3-env or similar (I do too, but haven't figured out a good solution yet)
plain-purple
plain-purpleOP3mo ago
hmm yeah hadn't really considered that, seems super easy to do until can find a version that works with t3-env. to make it type safe are you just hard coding the public env values in the function nothing fancy? hacked together this for now, works pretty well!
// env.ts
const getClientEnv = createServerFn({ method: "GET" }).handler(async () => {
return {
env: {
PUBLIC_APP_NAME: env.PUBLIC_APP_NAME,
PUBLIC_APP_URL: env.PUBLIC_APP_URL,
...
},
};
});

export const clientEnvQueryOptions = queryOptions({
queryKey: ["env"],
queryFn: getClientEnv,
staleTime: Number.POSITIVE_INFINITY,
});

export const useClientEnv = () => {
const { data, ...queryResult } = useSuspenseQuery(clientEnvQueryOptions);

return {
...queryResult,
env: data?.env,
};
};

// __root.ts
beforeLoad: async ({ context: { queryClient } }) => {
const { env } = await queryClient.ensureQueryData(clientEnvQueryOptions);

return {
env,
...
};
},
// env.ts
const getClientEnv = createServerFn({ method: "GET" }).handler(async () => {
return {
env: {
PUBLIC_APP_NAME: env.PUBLIC_APP_NAME,
PUBLIC_APP_URL: env.PUBLIC_APP_URL,
...
},
};
});

export const clientEnvQueryOptions = queryOptions({
queryKey: ["env"],
queryFn: getClientEnv,
staleTime: Number.POSITIVE_INFINITY,
});

export const useClientEnv = () => {
const { data, ...queryResult } = useSuspenseQuery(clientEnvQueryOptions);

return {
...queryResult,
env: data?.env,
};
};

// __root.ts
beforeLoad: async ({ context: { queryClient } }) => {
const { env } = await queryClient.ensureQueryData(clientEnvQueryOptions);

return {
env,
...
};
},
thanks for the help
harsh-harlequin
harsh-harlequin3mo ago
make sure you create either two files for env in the server and client, that solution worked for me
No description
plain-purple
plain-purpleOP3mo ago
the issue isn't really with t3-env its that I don't want the environment variables baked in at build time which is how vite handles it by default

Did you find this page helpful?