xChar
·5 months ago

用 zod 来验证环境变量,可以做到两件事:

  1. 如果缺少任何必需的环境变量,就在启动时使 app 崩溃
  2. 为环境变量添加类型定义,以便我们可以在 IDE 中获得自动完成和类型检查

直接上代码:

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)

Remix 可以这样:

// 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}`)
  }
}

然后在 entry.server.ts 中导入:

import '~/env.server'

Bonus

或者可以直接用 t3 env,原理都是一样的。

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 comments...