Open manucorporat opened 1 year ago
additional information:
sveltekit offers a different way to import environment variables that ensures private env isn't leaked to the client build. https://kit.svelte.dev/docs/server-only-modules#private-environment-variables
I made an experiment a while ago where a vite plugin is used to offer similar protections in a more generic way, albeit with slightly different DX (in kit you can import env, in that plugin you have to import each var)
https://github.com/svitejs/vite-plugin-env-import/tree/main/packages/vite-plugin-env-import
I'd get rid of the import.meta.env
built-ins because they don't work in library mode. If you build a library using import.meta.env
and then distribute it, it will not work with other bundlers. I built esm-env
as a replacement we use in SvelteKit which works both for libraries and apps
I agree with the VITE_
to PUBLIC_
rename
process.env
can be nice because it's set dynamically at runtime whereas import.meta.env
is replaced dynamically at build time. We need both as there are cases where one or the other must be preferred. SvelteKit made this dynamic vs static usage more explicit, which we may be able to do in Vite as well with an approach similar to the one Dominik shared
I understand it gets replaces at build time when building the library, no?, process.env
is a node-only idiom too. Sveltekit solution is interesting, are those server-only files commited? one of the nice things about the .env files is that it's well-known to not be committed
We can show the depreciation warning to Vite users to not use process.env
still we can copy the envs into both process.env
and import.meta.env
.
The problem with not using process.env
is that it's the only way TODAY to keep secret environment variables private!
btw @benmccann in Qwik, we also have a similar solution for isServer, isBrowser:
import {isServer} from '@builder.io/qwik/build';
i was thinking more of custom environment variables, Sveltekit solution with modules is very interesting though
I think to remove the confusion on which envs gets loaded in which environment, we can instead add CLIENT_
prefix, which will be loaded only on the client build, while SERVER_
prefix will be loaded only on server builds. AND, the PUBLIC_
prefix will be loaded on both client and server.
Yes, good point about process.env
being Node-only. The way we handle that in SvelteKit is by allowing the adapter for each hosting platform to pass in the environment variables. E.g. here's where we do it in our Vercel adapter:
I'm not sure how that would work outside a meta framework. Perhaps Vite could define a function like getEnv
that each framework would need to provide an implementation of or something. But I'm not sure there's a whole lot of benefit of standardizing that across frameworks and it seems just as well that this is something that's implemented by each framework.
The other interesting thing SvelteKit does is make sure you don't access any private env variables from public-facing code. You can only reference them if server
is in the directory or file name. Potentially that could be refactored out into a Vite plugin which could be shared across projects.
I find the VITE_
prefixing to be quite annoying.
In my case, I wanted to show the git sha1/branch to easily tell what version is deployed. By default this required re-exporting the CI environment variables.
export VITE_VERCEL_GITHUB_COMMIT_REF=$VERCEL_GITHUB_COMMIT_REF
export VITE_VERCEL_GITHUB_COMMIT_SHA=$VERCEL_GITHUB_COMMIT_SHA
Changing the prefix to PUBLIC_
doesn't really help in this case.
It's still a lot of error-prone copy/pasting.
Additionally, I would sometimes introduce a import.meta.env.ABC
in a typescript file, but forget to update the build script. So it would silently fail.
My current solution is to centralize all client-only environment variables to one file.
client-env-vars.ts
export const VERCEL_GITHUB_COMMIT_REF = process.env.VERCEL_GITHUB_COMMIT_REF
export const VERCEL_GITHUB_COMMIT_SHA = process.env.VERCEL_GITHUB_COMMIT_SHA
Components have to go through this file to use environment variables.
DebugPage.vue
import { VERCEL_GITHUB_COMMIT_REF, VERCEL_GITHUB_COMMIT_SHA } from '../client-env-vars'
Vite directly imports this file and replaces the process.env.*
via define
:
vite.config.ts
import * as envVars from './client-env-vars'
const define: Record<string, string | undefined> = {}
for (const [key, value] of Object.entries(envVars)) {
define[`process.env.${key}`] = JSON.stringify(value)
}
export default defineConfig({
define
})
I don't know what the best solution is, but I think ideally:
I appreciate the diverse perspectives. I would like to add I find the convenience of @t3-oss/env-nextjs or @t3-oss/env-core very commendable as well. It ensures type-safety for all environment variables.
Description
Today, variable environment handling in Vite is made of the following moving parts:
.env
files were the APP environment variables are definedimport.meta.env
process.env
VITE_
prefix that is used to filter which environment variables show up inimport.meta.env
The current state of the art, relies on
import.meta.env
andprocess.env
(a Node-only API) to correctly (and securely) integrate environment variables in a vite-powered application.Variables starting with
VITE_
will be included inimport.meta.env
in both client and server (SSR) build, while variable without the prefix will be included also be included inprocess.env
only in the server build.I want to argue that while the docs could be improved, the API is inheritely confusing, we have seen developers at qwik include their secrets in a environment variable prefixed with
VITE_
because they thoughtimport.meta.env
was the only API available to access variables.Suggested solution
process.env
is deprecated and never recommendedVITE_
toPUBLIC_
, so it states clearly the intention of the prefix,VITE
while on-brand, it does not contain semantic.import.meta.env
for SSR build:import.meta.env
will include ALL the environment variables, with and without thePUBLIC_
prefiximport.meta.env
will include ONLY the environment variables with thePUBLIC_
prefix (like it does currently)This way, there is no need for
process.env
in modern source code, and the cognitive load is lower for developers since there is a single recommended API to get the variablesimport.meta.env
and a clear intention for variables supposed to be public and therefore included in the browser build.Alternative
No response
Additional context
No response
Validations