Managing environment variables in your project is an essential task, but it can also be challenging. That’s why we have included a complete setup for environment variables in this project. This setup comes with validation and type-checking using the zod library.
All the code related to environment variables is located in the env.js and src/lib/env.js files. The env.js read the APP_ENV variable and loads the correct .env file, then defines the zod schema for the environment variables for client and build-time, parses the _env object, and returns the parsed object, or throws errors in case of invalid or missing variables.
To increase security, we are splitting environment variables into two parts:
Client Variables: Variables that are safe to be exposed to the client and used in your src folder. These variables are passed to the client side using the extra configuration in the app.config.ts file.
Build Time Variables: Variables that we don’t need on the client-side and are only used in the app.config.ts, for example SENTRY_AUTH to upload the source maps to Sentry.
By using this pre-configured setup for environment variables, you can focus on building your project without worrying about managing and validating your environment variables.
To add a new environment variable to the project, follow these steps:
Add the new environment variable to the correct zod schema inside the env.js file based on this simple rule :
If the variable is used in the src folder, add it to the client schema, otherwise add it to the buildTime schema.
This will ensure that the new variable is validated correctly. and make sure we are only sending the correct vars to the client side
Here’s an example:
Add the new environment variable to the correct env object inside the env.js, _clientEnv for client variables and _buildTimeEnv for build time variables. Here’s an example:
Add the new environment variable to your .env files. Make sure to include it in all relevant files (development, staging, and production). Here’s an example:
Make sure to run pnpm prebuild to load the new values.
The new environment variable is now ready to use in your project. You can access it in your code using the Env object, like this:
Use APP_ENV to load the correct .env file :
As mentioned earlier, zod is used to validate environment variables at runtime and build time. If there are any missing or invalid variables, you’ll see an error message with information on what needs to be fixed. Here’s an example error message:
How it works
✅ Validate and parse environment variables
If you take a look at the env.js file, you will notice that the file is split into three main parts as shown below:
In the first part We load the correct .env file based on the APP_ENV variable using dotenv package. If the APP_ENV variable is not defined, we default to development.
we define some static variables for the app such as the app name, bundle Id and package. While these variables can be added to the .env files, we recommend keeping them in the env.js file as they are not meant to change. To handle different app variants, you can add suffixes to these variables using the withEnvSuffix function.
In the second part, we define the zod schema for the environment variables.
We split the environment variables into two parts:
Client Variables: Variables that are safe to be exposed to the client and used in the src folder.
Build Time Variables: Variables that we don’t need on the client-side and are only used in the app.config.ts, for example, SENTRY_AUTH to upload the source maps to Sentry.
These schemas are used to validate the environment variables. All the environment variables should be added to the correct schema.
We use the z.infer utility to infer the environment variables’ types from the schema and use it to define the _clientEnv and _buildTimeEnv objects’ type. This means that if you add a new environment variable to the schema, you will get a type error if you don’t add it to the correct _clientEnv and _buildTimeEnv object as well, and vice versa.
Finally, in the third part, we merge variables to _env, pare it using the zod schema, and return the parsed object as well as the client environment variable, or throw errors in case of invalid or missing variables.
✅ Use and send environment variables to the client
Now it’s as easy as importing Env , ClientEnv and withEnvSuffix from the ./env.js file and use inside our app.config.ts, and finally sending client env vars to the client side using extra property.
✅ Type checking for client environment variables
Here, we added a separate file to export all variables that have already been passed in the extra property to the client side. We added a little bit of magic to make it type-safe and easy to use.
Now the environment variables are ready to use in your project. You can access them in your code by importing Env from @env and using it like this: