ClientOptions interface in T3 Env source code explained
In this article, we analyse the ClientOptions interface provided for the client object in createEnv parameter, a function in T3 Env. A simple usage of t3-env is provided below:
export const env = createEnv({ /* * Serverside Environment variables, not available on the client. * Will throw if you access these variables on the client. */ server: { DATABASE_URL: z.string().url(), OPEN_AI_API_KEY: z.string().min(1), }, /* * Environment variables available on the client (and server). * * 💡 You'll get type errors if these are not prefixed with NEXT_PUBLIC_. */ client: { NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1), }, /* * Due to how Next.js bundles environment variables on Edge and Client, * we need to manually destructure them to make sure all are included in bundle. * * 💡 You'll get type errors if not all variables from `server` & `client` are included here. */ runtimeEnv: { DATABASE_URL: process.env.DATABASE_URL, OPEN_AI_API_KEY: process.env.OPEN_AI_API_KEY, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, },});
We are interested in finding out the types/interfaces of client object.
Straight away, I could tell we could expect this type along the lines of Record<String, ZodType>, but the way t3-env defines is different. Look at the below type picked from T3 Env source code.
export interface ClientOptions< TPrefix extends string | undefined, TClient extends Record<string, ZodType>,> { /** * The prefix that client-side variables must have. This is enforced both at * a type-level and at runtime. */ clientPrefix: TPrefix;/** * Specify your client-side environment variables schema here. This way you can ensure the app isn't * built with invalid env vars. */ client: Partial<{ [TKey in keyof TClient]: TKey extends `${TPrefix}${string}` ? TClient[TKey] : ErrorMessage<`${TKey extends string ? TKey : never} is not prefixed with ${TPrefix}.`>; }>;}
You will find that this uses generic type and TClient is of type Record<string, ZodType>, but client does not have this type, instead it has a check in place to ensure your key defined in client is prefixed with whatever you define in ClientPrefix.
Say, for example, you have defined your prefix as “NEXT_PUBLIC_” and you try to define some variable with a key that is not prefixed with “NEXT_PBULIC_”, you will see an error along the lines “{variable} is not prefixed with “NEXT_PBULIC_”
This is powerful in frameworks like Next.js where you don’t want to accidentally expose server side varaibles to the client side.
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.