5 min read
In Node.js applications, we often use environment variables for configuration. We can access them via
process.env, but we have to be careful. We don't know if the variable we are looking for is actually there, and
process.env always returns a string. Therefore, we often need to convert the value to the type we need.
If something is wrong with the environment (e.g., a variable is missing or has the wrong type), we often don't notice it until we use the variable at runtime, and the error message is often not very helpful.
In this post, we will try to solve this problem using TypeScript and Zod.
Let's say we have a simple application that loads its environment using the dotenv package. The application requires the following environment variables:
To specify the environment variables, we create an
.env file in the root directory of our project.
Now, we can load the environment variables using dotenv.
After loading the environment variables, we can access them through
But here, our problems begin. We are unsure if the variable exists, and we receive it as a string. Therefore, the following code will not successfully pass the TypeScript compiler.
To fix this, we need to verify the existence of the variable and convert it into a number.
We have to do this for every variable we need. This is not only annoying, but also error-prone. If we forget to check a variable, we will only notice it when we run the application.
To solve this problem, we will create a type-safe version of
First, we create a new file called
env.ts, which loads, validates and converts our environment variables.
The code above loads the environment, as we have seen before. After that, it creates a schema with Zod. The schema defines the structure of our environment variables. We will look at the schema in a moment, but for now, we define it as an empty object. Once the schema is defined, we parse the environment variables using the schema. If the parsing fails, we log an error and exit the process. Finally, we export the parsed environment variables.
The beauty of this approach is that we only have to do this once, and we can import it from wherever we want. The best part is that it is completely type-safe.
We can access the environment variables as follows:
We no longer need to check if the variable exists or convert it. Zod handles it for us, and TypeScript no longer yells at us.
If an environment variable is missing or has the wrong format, we will encounter an error upon application startup. Now let's take a look at the schema.
- HOST: The host is a and .
- PORT: The port must be an and .
- EMAIL: The email is a and . .
- URL: The URL is a and . .
- NODE_ENV: The node environment is an and must be one of the following values: , or . .
As we can see, Zod makes it really simple to define a complex schema for our environment variables, and this is just the tip of the iceberg for what can be done with Zod. If you want to learn more about Zod, check out the documentation or the wonderful course by Matt Pocock.
In this post, we have seen how to create a type-safe version of
process.env with TypeScript and Zod. This approach is not only type-safe but also makes working with environment variables easier.
The approach we have used in this article works fine for most applications, but there are edge cases where it does not work as well, especially in frameworks where the environment is used on both the server and the client. In these cases, it is better to use a library designed specifically for this purpose, such as T3 Env.
Posted in: typescript, zod, environment