Environment Variables and Configuration
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/core/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 theextra
configuration in theapp.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 exampleSENTRY_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.
This setup is highly inspired by T3 Stack 👌
Adding a new environment variable to the project.
To add a new environment variable to the project, follow these steps:
- Add the new environment variable to the correct
zod
schema inside theenv.js
file based on this simple rule : If the variable is used in thesrc
folder, add it to theclient
schema, otherwise add it to thebuildTime
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
, andproduction
). Here’s an example:
::: note if you are not pushing env files to your repo(recomended), please make sure to check the App releasing process to see how to create the env file on the fly before the prebuild script in the github actions. :::
- 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: