next-zodenv makes dealing with environment variables in Next.js safer. Its concept is heavily inspired by envsafe created by KATT, but there are some nuances; you define a schema for your process.env
with Zod's constructs! In addition, because it's developed to work with Next.js in mind, integrating next-zodenv with Next.js is a piece of cake (it can be used with other environments, though).
Here are the basic ideas of next-zodenv:
- 💎 Express environment variables declaratively using Zod
- ✅ Validate that the specified environment variables are not missing on build time
- 🪄 Transform environment variables to the type your application expects
- 🤝 Work on Node.js and the browser
npm install next-zodenv zod
Suppose that you have the following environment variables:
FOO="server_only_variable"
PORT=3000
API_URL="https://example.com/api"
NEXT_PUBLIC_VAR="public_variable"
You can define a schema for your environment variables:
import { zenv } from 'next-zodenv'
import { z } from 'zod'
const env = zenv({
FOO: z.string(),
PORT: z.preprocess(Number, z.number().int().gte(1).lte(65535)),
API_URL: z.string().url(),
})
env.FOO // string
env.PORT // number (1-65535)
env.API_URL // string (URL)
env.NEXT_PUBLIC_VAR // undefined
Note that types other than string must be transformed with z.preprocess
beforehand. This is because environment variables are always string and we need to transform them to the type Zod's schema expects.
For simple cases like the above, next-zodenv offers built-in validators defined using Zod's constructs:
import { zenv, str, port, url } from 'next-zodenv'
const env = zenv({
FOO: str(),
PORT: port(),
API_URL: url(),
})
The complete list of built-in validators is as follows:
Validator | Zod schema |
---|---|
str() |
z.string() |
bool() |
z.preprocess((val) => val === 'true', z.boolean()) |
num() |
z.preprocess(Number, z.number()) |
port() |
z.preprocess(Number, z.number().int().min(1).max(65535)) |
url() |
z.string().url() |
email() |
z.string().email() |
In order to expose environment variables to the browser in Next.js, you need to pass process.env.NEXT_PUBLIC_*
to the value
prop like this:
const env = zenv(
{
NEXT_PUBLIC_VAR: {
zodType: z.string(),
value: process.env.NEXT_PUBLIC_VAR,
},
},
)
env.NEXT_PUBLIC_VAR // available in the browser
To validate on build time and stop the build process if there are missing environment variables, load your schema in next.config.js
:
// next.config.js
const { env } = require('./env/server.js');
// ...