DEV Community

thinkThroo
thinkThroo

Posted on

Here’s how Lobechat applies typesafety to its environment variables in Next app

In this article, you will learn about T3 env and its usage with an example. We also analyze T3 env usage in Lobechat.

Image description

T3 env

When you visit T3 env website, it has this hero title “Framework agnostic validation for type-safe environment variables.” with this description — “Never build your apps with invalid environment variables again. Validate and transform your environment with the full power of Zod.”

You can use T3 env to apply type-safe validations on your environment variables so you don’t end up deploying an application with invalid env variables. but how do you apply these validations? let’s find out.

Installation

Use the below command to install T3 env in your project.

# Core package, no framework specific features
pnpm add @t3-oss/env-core zod
# or, with options preconfigured for Next.js
pnpm add @t3-oss/env-nextjs zod
Enter fullscreen mode Exit fullscreen mode

Usage

T3 env usage is simple, you would first have to define your schema as shown below:

// src/env.mjs
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
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

Github docs for T3 env provides Next.js based example. Pay extra attention to the comments provided in the above example. Since Next.js is a full-stack framework, you have env variables for server and client and you need to be careful to not expose your server side enviroment variables to

client.

T3 env requires you to define your server env types in server object in schema.

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

Similarly, define the types for the client side environment variables

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

So far, we only defined the variables and their types on the client and server side. Next step is to define runtimeEnv.

/*
* 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

Use the schema in your app with autocompletion and type inference

// src/app/hello/route.ts
import { env } from "../env.mjs";
export const GET = (req: Request) => {
 const DATABASE_URL = env.DATABASE_URL;
 // use it…
};
Enter fullscreen mode Exit fullscreen mode

You can import env in the file that requires it and you now have type-safety applied to your env variables with

auto-completion.

Lobechat usage of T3 env

Lobechat defines the schema, as explained above, in src/config/db.ts.

import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';
export const getServerDBConfig = () => {
 return createEnv({
 client: {
 NEXT_PUBLIC_ENABLED_SERVER_SERVICE: z.boolean(),
 },
 runtimeEnv: {
 DATABASE_DRIVER: process.env.DATABASE_DRIVER || 'neon',
 DATABASE_TEST_URL: process.env.DATABASE_TEST_URL,
 DATABASE_URL: process.env.DATABASE_URL,
DISABLE_REMOVE_GLOBAL_FILE: process.env.DISABLE_REMOVE_GLOBAL_FILE === '1',
KEY_VAULTS_SECRET: process.env.KEY_VAULTS_SECRET,
NEXT_PUBLIC_ENABLED_SERVER_SERVICE: process.env.NEXT_PUBLIC_SERVICE_MODE === 'server',
 },
 server: {
 DATABASE_DRIVER: z.enum(['neon', 'node']),
 DATABASE_TEST_URL: z.string().optional(),
 DATABASE_URL: z.string().optional(),
DISABLE_REMOVE_GLOBAL_FILE: z.boolean().optional(),
KEY_VAULTS_SECRET: z.string().optional(),
 },
 });
};
export const serverDBEnv = getServerDBConfig();
Enter fullscreen mode Exit fullscreen mode

This serverDBEnv is used in server/core/db.ts.

Usage example:

import { serverDBEnv } from '@/config/db';
//
let connectionString = serverDBEnv.DATABASE_URL;
Enter fullscreen mode Exit fullscreen mode

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

2. https://github.com/lobehub/lobe-chat/blob/main/src/config/db.ts

3. https://env.t3.gg/

Top comments (0)