mancuoj

mancuoj

Better late than never.
github
twitter

Safely read your environment variables using Zod

To validate environment variables using zod, you can achieve two things:

  1. If any required environment variables are missing, crash the app on startup.
  2. Add type definitions for environment variables so that we can get auto-completion and type checking in our IDE.

Let's dive into the code:

const envSchema = z.object({
  SESSION_SECRET: z.string().min(1),
  DOMAIN_NAME: z.string().min(1),
})

declare global {
  namespace NodeJS {
    interface ProcessEnv extends TypeOf<typeof envSchema> {}
  }
}

envSchema.parse(process.env)

For Remix, you can do it like this:

// env.server.ts
import { TypeOf, z } from 'zod'

const envSchema = z.object({
  SESSION_SECRET: z.string().min(1),
  DOMAIN_NAME: z.string().min(1),
})

declare global {
  namespace NodeJS {
    interface ProcessEnv extends TypeOf<typeof envSchema> {}
  }
}

try {
  envSchema.parse(process.env)
} catch (err) {
  if (err instanceof z.ZodError) {
    console.error(err.flatten())
    const { fieldErrors } = err.flatten()
    const errorMessage = Object.entries(fieldErrors)
      .map(([field, errors]) => (errors ? `${field}: ${errors.join(', ')}` : field))
      .join('\n  ')
    throw new Error(`Missing environment variables:\n  ${errorMessage}`)
  }
}

Then import it in entry.server.ts:

import '~/env.server'

Bonus#

Alternatively, you can directly use t3 env, the principle is the same.

import { createEnv } from "@t3-oss/env-core";
import { z } from "zod";
 
export const env = createEnv({
  server: {
    DATABASE_URL: z.string().url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
 
  /**
   * The prefix that client-side variables must have. This is enforced both at
   * a type-level and at runtime.
   */
  clientPrefix: "PUBLIC_",
 
  client: {
    PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
  },
 
  /**
   * What object holds the environment variables at runtime. This is usually
   * `process.env` or `import.meta.env`.
   */
  runtimeEnv: process.env,
 
  /**
   * By default, this library will feed the environment variables directly to
   * the Zod validator.
   *
   * This means that if you have an empty string for a value that is supposed
   * to be a number (e.g. `PORT=` in a ".env" file), Zod will incorrectly flag
   * it as a type mismatch violation. Additionally, if you have an empty string
   * for a value that is supposed to be a string with a default value (e.g.
   * `DOMAIN=` in an ".env" file), the default value will never be applied.
   *
   * In order to solve these issues, we recommend that all new projects
   * explicitly specify this option as true.
   */
  emptyStringAsUndefined: true,
});
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.