用 zod 来验证环境变量,可以做到两件事:
- 如果缺少任何必需的环境变量,就在启动时使 app 崩溃
- 为环境变量添加类型定义,以便我们可以在 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,
});