T
TanStack4mo ago
correct-apricot

Environment variables in built application

Hi Tanner and friends– I've been building a new web application using the latest version of tanstack start. I've got a .env file in my root directory and the vinxi dev command is able to read from it no issue When I run vinxi build and then vinxi start - I end up with an application where the env vars are not being used. I've found that hardcoding them to the start command MY_SECRET=abc123 vinxi start allows me to access the env var from the built code– I'm curious why it's not automatically using the .env file like the dev server does? Is this a node issue, vinxi issue, start issue, or just a misunderstanding on my end of how this works? (I'm not necessarily blocked by this since I'm able to pass all my needed environment variables as part of the initial start command– but I'm curious as to why the vinxi start command isn't automatically loading .env for me and if there's a more graceful way to load them without needing to have them all listed out in front of my vinxi start command. Does that make sense?) Thanks!
10 Replies
fascinating-indigo
fascinating-indigo4mo ago
Are you prefixing with VITE_ for the frontend? Vinxi is vite under the hood
correct-apricot
correct-apricotOP4mo ago
I am not– the variables I'm looking to use are all secrets and only used by server functions
fascinating-indigo
fascinating-indigo4mo ago
you using turbo? if so you have to add to turbo.json if using that to build, otherwise I'd need more info
correct-apricot
correct-apricotOP4mo ago
I don't believe so– I'm using the start-supabase-basic example without any additions I updated the two provided .env variables from PleaseChangeMe to my appropriate values Running the dev server then works as intended Running build, then running start, results in the error:
Server Fn Error!

Error: Your project's URL and Key are required to create a Supabase client!
Server Fn Error!

Error: Your project's URL and Key are required to create a Supabase client!
Running SUPABASE_URL=abc123 SUPABASE_ANON_KEY=abc123 PUBLIC_URL=http://localhost:3000 vinxi start (with my actual url and key) fixes the error My question is do I need to do it that way– or is there a way to get vinxi start to just read from my .env file automatically like the dev server does
environmental-rose
environmental-rose4mo ago
currently on the same boat, i did notice that the .env are undefined when using the build output (but are working fine in dev) how are you reading the .env variables, are you using process.env or import.meta.env?
export function getSupabaseServerClient() {
const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_ANON_KEY;

return createServerClient(SUPABASE_URL ?? "", SUPABASE_ANON_KEY ?? "", {
cookies: {
getAll() {
return Object.entries(parseCookies()).map(([name, value]) => ({
name,
value,
}));
},
setAll(cookies) {
for (const cookie of cookies) {
setCookie(cookie.name, cookie.value);
}
},
},
});
}
export function getSupabaseServerClient() {
const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_ANON_KEY;

return createServerClient(SUPABASE_URL ?? "", SUPABASE_ANON_KEY ?? "", {
cookies: {
getAll() {
return Object.entries(parseCookies()).map(([name, value]) => ({
name,
value,
}));
},
setAll(cookies) {
for (const cookie of cookies) {
setCookie(cookie.name, cookie.value);
}
},
},
});
}
i have had to modify getSupabaseServerClient and add the VITE_ prefix on the supabase variables - just not sure why this is needed if the supabase client is only called on createServerFn() like this
...
beforeLoad: async () => {
const user = await fetchUser();

return {
user,
};
},
...
...
beforeLoad: async () => {
const user = await fetchUser();

return {
user,
};
},
...
const fetchUser = createServerFn({ method: "GET" }).handler(async () => {
const supabase = getSupabaseServerClient();
const { data, error: _error } = await supabase.auth.getUser();

if (!data.user?.email) {
return null;
}

return {
email: data.user.email,
};
});
const fetchUser = createServerFn({ method: "GET" }).handler(async () => {
const supabase = getSupabaseServerClient();
const { data, error: _error } = await supabase.auth.getUser();

if (!data.user?.email) {
return null;
}

return {
email: data.user.email,
};
});
fascinating-indigo
fascinating-indigo4mo ago
i actually do this:
/**
* Get an environment variable for the client
*
* @param key A key to resolve
* @param options Options for the environment variable
* @returns A resolved value for the key
*/
function clientEnv(key: string): string {
return import.meta.env[key] ?? '';
}

/**
* Get an environment variable for the server
*
* @param key A key to resolve
* @param options Options for the environment variable
* @returns A resolved value for the key
*/
function serverEnv(key: string): string {
return process.env[key] ?? '';
}

/**
* Get an environment variable from the client or server
*
* @param key A key to resolve
* @param options Options for the environment variable
* @returns A resolved value for the key
*/
function resolveEnv(keys: string[]): string {
return keys.reduce((acc, key) => {
const output = clientEnv(key) || serverEnv(key);

if (output) {
return output;
}

return acc;
}, '');
}
/**
* Get an environment variable for the client
*
* @param key A key to resolve
* @param options Options for the environment variable
* @returns A resolved value for the key
*/
function clientEnv(key: string): string {
return import.meta.env[key] ?? '';
}

/**
* Get an environment variable for the server
*
* @param key A key to resolve
* @param options Options for the environment variable
* @returns A resolved value for the key
*/
function serverEnv(key: string): string {
return process.env[key] ?? '';
}

/**
* Get an environment variable from the client or server
*
* @param key A key to resolve
* @param options Options for the environment variable
* @returns A resolved value for the key
*/
function resolveEnv(keys: string[]): string {
return keys.reduce((acc, key) => {
const output = clientEnv(key) || serverEnv(key);

if (output) {
return output;
}

return acc;
}, '');
}
fascinating-indigo
fascinating-indigo4mo ago
I've also used this in our product API / app to make sure it won't start if no env: https://www.npmjs.com/package/envalid
npm
envalid
Validation for your environment variables. Latest version: 8.0.0, last published: 2 years ago. Start using envalid in your project by running npm i envalid. There are 575 other projects in the npm registry using envalid.
fascinating-indigo
fascinating-indigo4mo ago
and if not clear what resolveEnv is =>
const config = {
environment: resolveEnv(['VITE_ENVIRONMENT', 'ENVIRONMENT']),
domain: resolveEnv(['VITE_DOMAIN', 'DOMAIN']),
};
const config = {
environment: resolveEnv(['VITE_ENVIRONMENT', 'ENVIRONMENT']),
domain: resolveEnv(['VITE_DOMAIN', 'DOMAIN']),
};
Aka I sometimes map both to same config location and just have separate env vars... not super elegant but it works
other-emerald
other-emerald4mo ago
unwilling-turquoise
unwilling-turquoise4mo ago
I tried vinxi start and got same problem, the solution is using node direclty node --env-file=.env .output/server/index.mjs that works as expected

Did you find this page helpful?