Before starting
In this guide I will assume that we already have our project with TS and Zod configured correctly.
Defining the variables inside .env file
Defining the Zod schema
We will define the necessary environment variables for the correct operation of our project (_for this guide I will use the Vite nomenclature VITE__), we will take advantage of the power of zod to make certain validations regarding the types of data and possible values that we will support for them, for example we will indicate that the variable called VITE_ENVIRONMENT will only be able to support the following values: "offline", "production", "development", "test".
We will also define the variable VITE_JWT_DEV_TOKEN as optional since it will only be mandatory to be able to assign a token for the offline environment.
Enable auto-completion and typing
In this step we will enable capping and autocompletion by extending the base system interface we are using to manage our environment variables (process, import.meta etc).
For process.env
For import.meta.env
With this we will get that when typing process.env. or import.meta.env. our IDE or editor will be able to show us the available variables together with their possible values and types.
Validation of our environment variables at runtime when starting our app
To do this we will create a small function that will simply use the schema created on the previous step to validate the contents of our environment variables, so that if there is any error when performing such validation will be displayed by console.
This function should be called in the init of our app.
Adding complex validations
Now let's see how we can solve the following use case in which the variable VITE_JWT_DEV_TOKEN should be mandatory when the value of VITE_ENVIRONMENT is offline.
We will use the refine functionality offered by Zod as follows:
Final content of the env.ts file
Benefits
-
Type Safety: By integrating Zod with TypeScript, you ensure that environment variables are not only present but also of the correct type. This reduces runtime errors and enhances the reliability of the application.
-
Validations at Compile Time and Runtime: Zod allows for complex validation logic, which can be executed both during compile-time and runtime. This dual-phase validation ensures that your environment variables meet the necessary criteria before the application even starts, preventing potential issues in a production environment.
-
Improved Developer Experience: The auto-completion and typing support provided by this setup significantly improve the developer experience. When a developer types process.env. or import.meta.env., they immediately see available variables along with their types and possible values, making the development process faster and less prone to errors.
-
Easy Maintenance and Scalability: With a clear structure and validation in place, adding new environment variables or modifying existing ones becomes straightforward. This makes the project easy to maintain and scale over time.
-
Runtime Validation Feedback: The function to check environment variables at runtime provides immediate feedback if there are any issues with the environment setup. This immediate feedback loop helps in quickly identifying and fixing configuration issues.
-
Customizable and Complex Validation Logic: The use of Zod's refine functionality allows for the implementation of complex and customized validation logic. For instance, making certain variables mandatory based on the value of another variable adds an extra layer of control over the environment setup.
-
Reduced Risk of Misconfiguration: By enforcing strict checks on environment variables, the risk of misconfigurations that could lead to security vulnerabilities or application failures is significantly reduced.
-
Enhanced Code Readability and Documentation: The explicit definition of environment variables and their types serves as a form of documentation, making the code more readable and understandable for new developers or when revisiting the code after a period.
-
Environment-Specific Configurations: This setup easily supports different configurations for various environments (e.g., development, production, test), ensuring that each environment is correctly configured with its specific needs.
In conclusion, integrating Zod with TypeScript for managing environment variables provides a robust, error-resistant, and developer-friendly approach to configuration management in software projects.
Sources:
-
Use Zod to parse your environment variables once, then use that inference to type process.env throughout your app.
ā Matt Pocock (@mattpocockuk) February 23, 2024
Also, a beautiful example of how you can extend an interface from a VALUE, not a type. pic.twitter.com/sxgIxm60sg -
+1
ā Kent C. Dodds š (@kentcdodds) February 23, 2024
This is what we do in the Epic Stack https://t.co/89tz2ZLwtJ https://t.co/gQq0yhyr8l
Thanks for reading me š