Hey there! If you're like me, you probably love using Zod for validating and parsing data objects. It's a lifesaver, right? But when it comes to environment variables (env vars), things can get a bit tricky. You want to parse them as quickly as possible during your application's boot-up phase and then use them confidently throughout your app. That's where NestJS's ConfigModule comes into play.
NestJS provides a handy ConfigModule that lets you register a global module to read your env file and load it into the process. But here's the catch: you need to validate those env vars to ensure they're correct. Luckily, the ConfigModule allows you to register a validate function. Here's a quick look at how you can set it up:
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, validate: /* Your function */ }),
]
})
export class AppModule {}The validate function receives the full process.env object and expects a validated, parsed object in return. This is where Zod comes in handy. You can use it to format and validate your env vars like this:
import { z } from "zod";
As a bonus, you can extend the ConfigService types with your Zod schema. This ensures that whenever you're using the ConfigModule, you have type safety. Here's how you can define your type:
import type { ConfigService } from "@nestjs/config";
export type IConfigService = ConfigService<z.infer<typeof envs>>;Once you've set up your types, you can use them in your modules like so:
@Module({
imports: [
DrizzlePostgresModule.registerAsync({
tag: DB_TAG,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: IConfigService) => ({
postgres: {
url: configService.getOrThrow("DATABASE_URL"),
},
}),
}),
],
})
export class DbModule {}validate function to ensure env vars are correct.