Managing environment variables effectively is crucial in any application, whether it’s a frontend or backend. Missing or misconfigured environment variables can lead to hard-to-debug runtime errors. Fortunately, the Zod library offers a simple yet powerful solution for schema validation, which can be leveraged to validate environment variables upfront. In this blog post, we'll explore how to use Zod to ensure all necessary environment variables are set correctly, preventing unexpected runtime issues.
Additionally, we will cover how to manage environment variables using a .env
file in a Node.js project with TypeScript.
What is Zod?
Zod is a TypeScript-first schema declaration and validation library. It provides a straightforward way to define schemas and validate data against these schemas. It can be used to validate inputs, responses, and, importantly, environment variables.
Why Validate Environment Variables?
Environment variables are used to configure various aspects of an application, such as database connections, API endpoints, and ports. If these variables are missing or misconfigured, your application might crash or behave unpredictably. Validating these variables at startup ensures that all required configuration is present and correctly formatted, providing an early failure mechanism that saves time and effort in debugging.
Step-by-Step Guide to Using Zod for Environment Variables
Let’s walk through how to set up Zod to validate environment variables in a TypeScript project and how to use a .env
file to manage these variables.
Step 1: Initialize Your Node.js Project
First, you need to initialize a new Node.js project if you haven't already. This will create a package.json
file to manage your project's dependencies and scripts.
npm init -y
Step 2: Install Dependencies
Next, install Zod and dotenv
in your project. You will also need express
, @types/express
, @types/node
, and ts-node
for a complete setup.
npm install zod dotenv express
npm install --save-dev @types/express @types/node ts-node typescript
Step 3: Set Up TypeScript Configuration
Ensure your tsconfig.json
is properly configured for TypeScript. Here is a basic example:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"],
"exclude": ["node_modules"]
}
Step 4: Create a .env
File
Create a .env
file in the root of your project. This file will store your environment variables.
PORT=3000
BASE_URL=http://localhost
Step 5: Configure dotenv
in Your Project
Create a new file, env.ts
, where you will configure dotenv
and define the schema for your environment variables using Zod.
import { z } from 'zod';
import dotenv from 'dotenv';
dotenv.config();
const envSchema = z.object({
PORT: z
.string()
.refine(
(port) => parseInt(port) > 0 && parseInt(port) < 65536,
"Invalid port number"
),
BASE_URL: z
.string()
.refine(
(url) => url.startsWith("http") || url.startsWith("https"),
"Invalid URL format"
),
});
type Env = z.infer<typeof envSchema>;
export const ENV: Env = envSchema.parse(process.env);
In this schema, we define that PORT
and BASE_URL
are required environment variables and should be strings. The PORT
must be a valid port number between 1 and 65535, and BASE_URL
must start with "http" or "https". The z.infer
utility type is used to infer the TypeScript type from the schema.
Step 6: Validate the Environment Variables
By calling envSchema.parse(process.env)
, Zod will validate the process.env
object against the defined schema. If any of the variables are missing or do not match the defined type, Zod will throw an error, causing your application to crash with a clear message about what’s wrong.
Step 7: Integrate the Validation in Your Application
To ensure the validation runs before the rest of your application, import the env.ts
file at the very start of your main entry file, usually index.ts
.
import { ENV } from './env';
// Rest of your application code
import express from 'express';
const app = express();
const port = Number(ENV.PORT);
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Server running at ${ENV.BASE_URL}:${port}`);
});
Step 8: Running Your TypeScript Project
Make sure to run your project using ts-node
. You can add a script to your package.json
to make this easier:
{
"scripts": {
"start": "ts-node src/index.ts"
}
}
Step 9: Demonstrate the Validation
To see Zod's validation in action, intentionally set an incorrect value in the .env
file. For example, set PORT
to an invalid number and BASE_URL
to a string that doesn't start with "http" or "https".
Modify your .env
file as follows:
PORT=70000
BASE_URL=localhost
Run your application using the start
script:
npm start
You should see an error message indicating the invalid values:
Invalid environment variables: [
{
"path": ["PORT"],
"message": "Invalid port number"
},
{
"path": ["BASE_URL"],
"message": "Invalid URL format"
}
]
Conclusion
Using Zod to validate your environment variables is a straightforward and effective way to prevent configuration-related issues. By defining a schema and validating your environment variables at startup, you can ensure that your application fails early and clearly, saving time and avoiding potential runtime errors. This approach can be applied to both frontend and backend environments, making it a versatile tool in your development toolkit.
Additionally, leveraging the dotenv
package allows you to manage your environment variables in a simple and organized manner, especially useful in a Node.js project with TypeScript.
Give it a try in your next TypeScript project and experience the peace of mind that comes with knowing your environment is correctly configured from the get-go. Happy coding!