DEV Community

thinkThroo
thinkThroo

Posted on

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,
 },
});
Enter fullscreen mode Exit fullscreen mode

We are interested in finding out the types/interfaces of client object.

client: {
 NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
 },
Enter fullscreen mode Exit fullscreen mode

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}.`>;
 }>;
}
Enter fullscreen mode Exit fullscreen mode

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.

Check this docs — https://env.t3.gg/docs/core#create-your-schema, this talks about prefix error.

About us:

At Thinkthroo, we study large open source projects and provide architectural guides. We have developed reusable Components, built with tailwind, that you can use in your project. We offer Next.js, React and Node development services.

Book a meeting with us to discuss your project.

Image description

References:

  1. https://github.com/t3-oss/t3-env/blob/main/packages/core/src/index.ts#L130

  2. https://env.t3.gg/docs/core#create-your-schema

Top comments (0)