t3-env
t3-env copied to clipboard
Should we allow `NODE_ENV` access on client?
With NextJS (probably same for most of the framework nowadays), only envs with PREFIX_
can be accessed in client-side, however, NODE_ENV
is technically accessible in browser too.
So, the question is, should the library expose the NODE_ENV
to the client-side?
One of the use-cases that I used personally is to show the Tailwind screen size indicator in NODE_ENV === 'development'
. As of now, I can work around the limitation by creating a NEXT_PUBLIC_NODE_ENV: process.env.NODE_ENV
in runtimeEnv
, but it feels weird.
What would be a better solution to you? Is there more of these that are available on the client that isn't prefixed?
What would be a better solution to you? Is there more of these that are available on the client that isn't prefixed?
Only NODE_ENV for NextJS from what I can tell.
I was thinking maybe we can validate & expose NODE_ENV by default, however, frameworks like Vite expose similar things with different name, so we probably can make it configurable by adding another prop called "both" or "base"? But I have a feeling that this might cause more trouble🤷♂️
This issue breaks tRPC - given the line below in {basedir}/app/nextjs/src/utils/api.ts
(using the t3-turbo starter for example):
export const api = createTRPCNext<AppRouter>({
config() {
return {
transformer: superjson,
links: [
loggerLink({
enabled: (opts) =>
/* Breaks here - but it works if we comment out this return value and replace it with `true` */
env.NODE_ENV === "development" ||
(opts.direction === "down" && opts.result instanceof Error),
}),
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
}),
],
};
},
ssr: false,
});
This results in an "Attempted to access server env on client" error when any query or mutation is triggered (at least during development).
To your question about other, non-prefixed variables: yes. Build flags - but the reason they're a special case isn't obvious at first.
See, given a pure NextJS use case, the answer is simple: build flags go in serverEnv
. Build flags work only when they have the exact key expected, so prefixes would break them. Building a NextJS app occurs in a NodeJS runtime - so typeof window === "undefined"
. All good, yeah?
But what about an Expo app? When building for native the distinction between server and client is less clear. Although Env vars used in the application bundle are always public, Env vars that are used during the build but NOT bundled are conceptually similar to Server Env vars. Now - ~I may be wrong about this, because I'm no expert on the Expo build process - I think~** Expo apps are built using the Hermes engine - so the build environment is, essentially, a browser. ~Again, I could be wrong, but it~ It certainly would explain the difficult-to-debug "Server var called on client" errors I'm seeing in Expo but not in NextJS (note that 95% of my code is shared between them).
This means if I want to add build flags for Expo, I have to do so under clientEnv
with a blank prefix. Cool - now I'm maintaining separate environments again, with duplicated values... but in one, they're marked clientEnv
, and another serverEnv
. Yuck!
I would propose a new property on the options argument: devEnv
or buildEnv
(or both).
Better yet, what about adding arbitrary new keys with a value like this one?:
type AdditionalEnv = {
test: (raw: ProcessEnv) => boolean;
env: { [key: string]: ZodType<any, any, any> }
}
Edit: This would also be a great tool for things like Vercel builds, etc. Essentially it would enable this tool to dynamically validate the environment at build time, allowing for divergent logic if, say, NODE_ENV === "test"
or !!VERCEL
return true.
**Edit: According to the docs:
"By compiling JavaScript into bytecode ahead of time, Hermes can improve your app start-up time. ".
Presumably, this means they are evaluating the code with Hermes at build, meaning that my hunch is indeed correct.
As a separate wish-list item, It would also be nice to have an idiomatic way of setting properties directly on process.env
for libraries that check the process.env
internally. The use case I'm thinking of is exemplified by this line in the t3-turbo starter.
But what about an Expo app?
I don't think we should use t3-env for RN app, it basically an overhead. A simple zod validation is more than enough for the usecase. To me, t3-env is mainly for fullstack-app like NextJS, Remix, Astro, & etc, other than it pretty much overkill.
As a separate wish-list item, It would also be nice to have an idiomatic way of setting properties directly on
process.env
for libraries that check theprocess.env
internally. The use case I'm thinking of is exemplified by this line in the t3-turbo starter.
IMO, mutating process.env.XXX
is a no go, and it's kind of out of scope for t3-env.
I been thinking about this lately, and my take is that maybe we shouldn't use t3-env for variable like NODE_ENV
, or even DEV
, PROD
etc from ViteJS, since they are mostly provide by the build-system/framework, so we should expect that the variables are always available, and those variables are mostly properly typed from what I can tell.
The point of using t3-env is mostly to ensure we as a developer provider the correct env variables as predefined, with some addition features like type-safe, and runtime checking for prefix-variables.
I been thinking about this lately, and my take is that maybe we shouldn't use t3-env for variable like NODE_ENV
, or DEV
, PROD
etc from ViteJS, since they are mostly provide by the build-system/framework, so we should expect that the variables are always available, and those variables are mostly properly typed from what I can tell.
The point of using t3-env is mostly ensure we as a developer provider the correct env variables as predefined, with some addition features like type-safe, and runtime checking for prefix-variables.
I can see an argument for not mutating process.env.xxx, and I understand if RN apps are out of scope. Perhaps they should be - after a bit of playing around it seems that anything other than setting process.env.xxx directly in babel.config.js breaks everything on the RN side.
Still, it's a nice pipe dream to have a packages/env as a single point of config/validation! To your point about being 'overhead' in an RN app - true, but not so in a cross-platform monorepo that's already using t3-env.
I tihnk built-ins are fine to just use as-is - they are typed and should be provided by the environment
But what about an Expo app?
I don't think we should use t3-env for RN app, it basically an overhead. A simple zod validation is more than enough for the usecase. To me, t3-env is mainly for fullstack-app like NextJS, Remix, Astro, & etc, other than it pretty much overkill.
I don't think it makes sense to say that a simple zod validation is more than enough for the usecase. It could also be enough for a NextJS app but t3-env proves useful in defining all the stack's env vars in the same place while providing validation and safe runtime checks. It's exactly the same usecase for a RN app sharing code with a backend in a monorepo. 🙂
Now Expo apps are built using the Hermes engine - so the build environment is, essentially, a browser. It certainly would explain the difficult-to-debug "Server var called on client" errors I'm seeing in Expo but not in NextJS (note that 95% of my code is shared between them).
I'm running into these errors as well but I don't think it's simply because Expo's build environment is like a browser, it still doesn't make sense that t3-env complains that a server variable is being accessed, as it shouldn't be.
I modified the onInvalidAccess
prop to log the name of the variable being accessed and this is what I got:
onInvalidAccess: (variable: string) => {
throw new Error(`Accessing server variable from client: ${variable}`);
},
Variable $$typeof
? That's not one of my server env vars for sure 🙃 seems like a bug to me. I don't see a reason why t3-env shouldn't work here.