Environment Variables

Environment Variable Validation

Create-T3-App uses zod for environment variable validation at runtime and buildtime by providing additional files (scaffolded with generic environment variables for the chosen libraries):

📁 src/env

┣ 📄 server.mjs

┣ 📄 client.mjs

┣ 📄 schema.mjs

A z.object is used as a schema, with each object key representing an environment variable and value representing a z method for validation. Each time a new environment variable is needed, it must be added to both .env[.local/.production etc] as well as schema.mjs.

Files

schema.mjs

This is the file that contains the Zod schemas, and by default, contains two exported schemas, serverSchema and clientSchema, as well as a clientEnv object.

Server Schema

Specify your server-side environment variables schema here.

// src/env/schema.mjs

export const serverSchema = z.object({
  // FOO: z.string(),
});

Client Schema

Specify your client-side environment variables schema here. To expose them to the client, prefix them with NEXT_PUBLIC_.

// src/env/schema.mjs

export const clientSchema = z.object({
  // NEXT_PUBLIC_BAR: z.string(),
});

clientEnv Object

You can’t destruct process.env as a regular object, so you have to do it manually here. This is because Next.js evaluates this at build time, and only used environment variables are included in the build.

// src/env/schema.mjs

export const clientEnv = {
  // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR,
};

server.mjs

This is the file that performs the validation on server-only environment variables (those which aren’t prefixed with NEXT_PUBLIC), using the z.object schema from schema.mjs. It is imported into next.config.mjs to use for buildtime validation. This file likely shouldn’t be modified unless you know what you’re doing.

client.mjs

Similar to server.mjs, this file performs the validation on client-side environment variables (those which are prefixed with NEXT_PUBLIC).

Add a new environment variable

To ensure your build never completes without the environment variables the project needs, you will need to add new environment variables in two locations:

.env

Added in the regular method of NAME=VALUE

schema.mjs

Added inside the clientSchema or serverSchema objects depending on if they are to be consumed client-side or in your backend, defining the type as a zod schema.

Example

I need to add a new environment variable to my project, with a name of POKEAPI_KEY and a value of 1234ABCD.

.env file:

# .env

# ... any other variables that are already here
POKEAPI_KEY=1234ABCD

schema.mjs file:

// src/env/schema.mjs

export const serverSchema = z.object({
  // ... any other variables that are already here
  POKEAPI_KEY: z.string(),
});

Now, schema validation will occur at runtime and build time to ensure the POKEAPI_KEY is present in my environment variables.

Type-safe Environment Variables

To utilise the schema containing environment variables in your code editor, you should import { env } from either /env/server.mjs or /env/client.mjs depending where they are being used. The env object is a type-safe parsed result of the relevant schema, allowing for auto-completion of environment variables in your code editor.