JavaScript Development Space

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.

How to Validate ENV Variables in TypeScript Using Zod

Step 1: Install Dependencies

To get started, install dotenv to load environment variables from a .env file and zod for validation.

npm install dotenv zod

Then, add dotenv configuration at the start of your code to ensure environment variables load correctly.

ts
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.

ts
1 import { z } from 'zod';
2
3 // Define the schema for environment variables
4 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.

ts
1 const parsedEnv = envSchema.safeParse(process.env);
2
3 if (!parsedEnv.success) {
4 console.error("Invalid environment variables:", parsedEnv.error.format());
5 process.exit(1); // Stop the application if validation fails
6 }
7
8 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:

ts
1 import { z } from 'zod';
2 import { envSchema } from "../path/to/envSchema"; // Adjust path as necessary
3
4 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 of envSchema.
  • ProcessEnv is extended with this inferred type, adding strict typing to process.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:

ts
1 // src/env.ts
2 import dotenv from 'dotenv';
3 import { z } from 'zod';
4
5 dotenv.config();
6
7 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 });
15
16 const parsedEnv = envSchema.safeParse(process.env);
17
18 if (!parsedEnv.success) {
19 console.error("Invalid environment variables:", parsedEnv.error.format());
20 process.exit(1);
21 }
22
23 export const env = parsedEnv.data;
24
25 // src/types/env.d.ts
26 import { z } from 'zod';
27 import { envSchema } from "../env"; // Adjust path as necessary
28
29 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.

JavaScript Development Space

© 2024 JavaScript Development Space - Master JS and NodeJS. All rights reserved.