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.
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
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,
},
});
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),
},
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),
},
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,
},
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…
};
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();
This serverDBEnv is used in server/core/db.ts.
Usage example:
import { serverDBEnv } from '@/config/db';
//
let connectionString = serverDBEnv.DATABASE_URL;
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.
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/
Subscribe to my newsletter
Read articles from Think Throo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by