Adding type safety to environment variables in NextJS

Bharathvaj Ganesan
3 min readFeb 5, 2023

I have been working with Next.js for sometime, but we used to find hard time with environment variables mainly when getting started to contribute to existing projects.

Projects usually have .env.example file that helps contributors in setting up the project in local environment where we might be creating .env.local file based on the example env. Reason for this is that .env.local will be not be tracked in git version control because it might contain sensitive data.

Challenges that one might encounter,

  • Unclear Runtime errors because env variables required are not set.
  • Unclear compile time errors because env variables required are not set.
  • Unexpected behaviour in the application because required variable for local env is not set
  • ….
  • ….

and the list grows…

These challenges are not limited to local, even while deploying to a remote environment like Vercel these env variables when not set will cause run time error but ideally this would have been better if are caught during build time itself.

Validating environment variables is an important step in the development process of any application. Environment variables are used to store sensitive information such as API keys and passwords that should not be stored directly in the codebase. It’s essential to validate these variables to ensure that the application is secure and functions correctly.

We can use the Zod library to validate environment variables. Zod is a powerful, fast and lightweight JavaScript library for validating and transforming data. It supports many data types, including strings, numbers, arrays, objects, and even custom data types.

Here is a step-by-step guide on how to validate environment variables using Zod in a Next.js application:

1. Install Zod:

In your Next.js application, run the following command to install Zod:

npm install zod

2. Create a schema for your environment variables:

Zod allows you to create a schema for your environment variables, which defines the structure and types of variables that you expect. Here’s an example of a schema for a few environment variables:

const zod = require('zod');

const envSchema = zod.object({
API_KEY: zod.string().required(),
API_SECRET: zod.string().required(),
PORT: zod.number().integer().required(),
});ty

3. Validate environment variables:

.parse method of Zod will parse the object and validates against the envSchema.

// You can't destruct `process.env` as a regular object, so you have to do it manually here.
export const env = envSchema.parse({
API_KEY: process.env.API_KEY,
API_SECRET: process.env.API_SECRET,
PORT: process.env.PORT,
});

Finally, you have to run the above ones when Next.js builds the application. You can create a file called env.js and have the above scripts within it, so that we can easily run them just by importing to next.config.js as shown below,

// next.config.js
import './src/env.js';

/** @type {import('next').NextConfig} */
module.exports = {
reactStrictMode: true,
images: {
domains: ['res.cloudinary.com'],
},
};

Whenever Next.js builds it will consume next.config.js configuration file and this file be executed at build time and validation happens.

BONUS: Consume environment variables:

You can also easily consume the env variables with better typing support.

import { env } from './env';

// Example
lib.init({
API_KEY: env.API_KEY,
API_SECRET: env.API_SECRET,
});

T3 stack template based on Next.js handles this out of the box for you. Please do check that out. You can also handle client and server environment variables separately for fine grain control so that your server secrets doesn’t expose to client.

const zod = require('zod');

const clientSchema = Zod
.object
// your client schema goes here
();
export const clientEnv = clientSchema({});

const serverSchema = Zod
.object
// your server schema goes here
();
export const serverEnv = serverSchema({});

Happy managing environment variables.

References

Originally published at https://www.bharathvaj.me on February 5, 2023.

--

--