How to Validate ENV Variables in TypeScript Using Zod
Validating environment variables in TypeScript is essential for ensuring that your application is running with the required configuration. You can validate environment variables in TypeScript by using packages like zod or joi, or even with custom code. Here’s a step-by-step guide on how to validate environment variables effectively using TypeScript and the zod package.
Step 1: Install Dependencies
To get started, install dotenv
to load environment variables from a .env
file and zod
for validation.
Then, add dotenv
configuration at the start of your code to ensure environment variables load correctly.
1 import dotenv from 'dotenv';2 dotenv.config();
Step 2: Setting Up Environment Variable Schema with Zod
Define the structure of your environment variables using Zod. This schema will specify the expected types and constraints, catching errors early.
1 import { z } from 'zod';23 // Define the schema for environment variables4 const envSchema = z.object({5 NODE_ENV: z.enum(["development", "production", "test"]),6 PORT: z.string().transform(Number).refine((port) => port > 1000 && port < 5000, {7 message: 'PORT must be a number between 1 and 65535',8 }),9 DATABASE_URL: z.string().url(),10 AUTH_SECRET: z.string(),11 });
In this example:
NODE_ENV
must be one of "development," "production," or "test."PORT
is a string parsed as a number, constrained to valid port numbers (1000–5000).DATABASE_URL
must be a valid URL.AUTH_SECRET
should be a non-empty string.
Step 3: Validating Environment Variables
Using the safeParse
function in Zod, you can validate environment variables and handle errors if they don’t match the expected schema.
1 const parsedEnv = envSchema.safeParse(process.env);23 if (!parsedEnv.success) {4 console.error("Invalid environment variables:", parsedEnv.error.format());5 process.exit(1); // Stop the application if validation fails6 }78 export const env = parsedEnv.data; // Export the validated variables for use in your app
The safeParse
method checks process.env
variables against the schema:
- If the validation fails, an error message will display, and the app will exit.
- If successful, the validated env object is exported.
Integrating Validation into process.env
To improve type safety, we can extend the ProcessEnv interface in TypeScript to include the types from our Zod schema. This will allow you to access process.env with the proper types everywhere in your code.
Step 4: Declaring Types Globally
To make the TypeScript types globally accessible, extend the ProcessEnv
interface with Zod’s inferred type. This type declaration should be placed in a .d.ts
file in your project, such as env.d.ts
.
Create a src/types/env.d.ts
file with the following content:
1 import { z } from 'zod';2 import { envSchema } from "../path/to/envSchema"; // Adjust path as necessary34 declare global {5 namespace NodeJS {6 interface ProcessEnv extends z.infer<typeof envSchema> {}7 }8 }
In this code:
- We use
z.infer<typeof envSchema>
to get the inferred type ofenvSchema
. ProcessEnv
is extended with this inferred type, adding strict typing toprocess.env
.
After this setup, TypeScript will ensure your code references valid and correctly typed environment variables.
Complete Code Example
Here is the full code example combining all steps:
1 // src/env.ts2 import dotenv from 'dotenv';3 import { z } from 'zod';45 dotenv.config();67 const envSchema = z.object({8 NODE_ENV: z.enum(["development", "production", "test"]),9 PORT: z.string().transform(Number).refine((port) => port > 1000 && port < 5000, {10 message: 'PORT must be a number between 1 and 65535',11 }),12 DATABASE_URL: z.string().url(),13 API_KEY: z.string(),14 });1516 const parsedEnv = envSchema.safeParse(process.env);1718 if (!parsedEnv.success) {19 console.error("Invalid environment variables:", parsedEnv.error.format());20 process.exit(1);21 }2223 export const env = parsedEnv.data;2425 // src/types/env.d.ts26 import { z } from 'zod';27 import { envSchema } from "../env"; // Adjust path as necessary2829 declare global {30 namespace NodeJS {31 interface ProcessEnv extends z.infer<typeof envSchema> {}32 }33 }
Benefits of Using zod
- Type Safety:
zod
ensures your environment variables are type-safe, preventing runtime errors due to misconfigured values. - Detailed Errors: Validation errors provide detailed messages, making it easy to identify which variables are missing or incorrectly set.
- Optional Defaults:
zod
allows default values for certain fields, reducing the need for extensive fallback logic.
Alternatives
While zod
is very flexible, here are other libraries for environment variable validation:
- Joi: A popular validation library that can also be used with TypeScript, although it requires slightly more configuration.
- dotenv-typesafe: A package that combines
dotenv
with TypeScript for safe and typed environment variables.
Final Thoughts
Using TypeScript for environment variable validation is a simple but effective way to catch configuration issues early. It’s particularly helpful in environments where your application relies on multiple services or API keys, as it ensures every required value is set correctly before the application starts.