环境变量
环境变量用于将密钥(如数据库密码、应用程序密钥或 API 密钥)存储在应用程序代码库之外。
此外,环境变量可用于为不同的环境提供不同的配置。例如,你可以在测试期间使用内存邮件程序,在开发期间使用 SMTP 邮件程序,在生产环境中使用第三方服务。
由于所有操作系统、部署平台和 CI/CD 管道都支持环境变量,它们已成为存储密钥和特定于环境的配置的事实标准。
在本指南中,我们将学习如何在 AdonisJS 应用程序中利用环境变量。
读取环境变量
Node.js 原生通过 process.env 全局属性 将所有环境变量作为对象公开,你可以按如下方式访问它们。
process.env.NODE_ENV
process.env.HOST
process.env.PORT使用 AdonisJS env 模块
通过 process.env 对象读取环境变量不需要 AdonisJS 端的任何设置,因为 Node.js 运行时支持它。但是,在本文档的其余部分,我们将使用 AdonisJS env 模块,原因如下:
- 能够从多个
.env文件存储和解析环境变量。 - 在应用程序启动时立即验证环境变量。
- 为已验证的环境变量提供静态类型安全。
env 模块在 start/env.ts 文件中实例化,你可以在应用程序的其他地方按如下方式访问它。
import env from '#start/env'
env.get('NODE_ENV')
env.get('HOST')
env.get('PORT')
// 当 PORT 未定义时返回 3333
env.get('PORT', 3333)与 Edge 模板共享 env 模块
如果你想在 edge 模板中访问环境变量,那么你必须将 env 模块作为全局变量与 edge 模板共享。
你可以在 start 目录中创建 view.ts 作为预加载文件,并在其中写入以下代码行。
TIP
这样做不会将 env 模块暴露给浏览器。env 模块仅在服务器端渲染期间可用。
// title: start/view.ts
import env from '#start/env'
import edge from 'edge.js'
edge.global('env', env)验证环境变量
环境变量的验证规则使用 Env.create 方法在 start/env.ts 文件中定义。
当你第一次导入此文件时,验证会自动执行。通常,start/env.ts 文件由项目中的某个配置文件导入。如果没有,那么 AdonisJS 将在启动应用程序之前隐式导入此文件。
Env.create 方法接受验证模式作为键值对。
- 键是环境变量的名称。
- 值是执行验证的函数。它可以是自定义内联函数或对预定义模式方法(如
schema.string或schema.number)的引用。
import Env from '@adonisjs/core/env'
/**
* App root 用于在项目根目录中定位 .env 文件。
*/
const APP_ROOT = new URL('../', import.meta.url)
export default await Env.create(APP_ROOT, {
HOST: Env.schema.string({ format: 'host' }),
PORT: Env.schema.number(),
APP_KEY: Env.schema.string(),
APP_NAME: Env.schema.string(),
CACHE_VIEWS: Env.schema.boolean(),
SESSION_DRIVER: Env.schema.string(),
NODE_ENV: Env.schema.enum([
'development',
'production',
'test'
] as const),
})静态类型信息
相同的验证规则用于推断静态类型信息。使用 env 模块时可以获得类型信息。

验证器模式 API
schema.string
schema.string 方法确保值是有效的字符串。空字符串会使验证失败,你必须使用 optional 变体来允许空字符串。
{
APP_KEY: Env.schema.string()
}
// 标记为可选
{
APP_KEY: Env.schema.string.optional()
}
// 按条件标记为可选
{
APP_KEY: Env.schema.string.optionalWhen(() => process.env.NODE_ENV === 'production')
}字符串值可以按其格式进行验证。以下是可用格式的列表。
host
验证值是有效的 URL 或 IP 地址。
{
HOST: Env.schema.string({ format: 'host' })
}url
验证值是有效的 URL。可选地,你可以通过允许 URL 没有 protocol 或 tld 来使验证不那么严格。
{
S3_ENDPOINT: Env.schema.string({ format: 'url' })
// 允许没有协议的 URL
S3_ENDPOINT: Env.schema.string({ format: 'url', protocol: false })
// 允许没有 tld 的 URL
S3_ENDPOINT: Env.schema.string({ format: 'url', tld: false })
}email
验证值是有效的电子邮件地址。
{
SENDER_EMAIL: Env.schema.string({ format: 'email' })
}schema.boolean
schema.boolean 方法确保值是有效的布尔值。空值会使验证失败,你必须使用 optional 变体来允许空值。
'true'、'1'、'false' 和 '0' 的字符串表示形式会被转换为布尔数据类型。
{
CACHE_VIEWS: Env.schema.boolean()
}
// 标记为可选
{
CACHE_VIEWS: Env.schema.boolean.optional()
}
// 按条件标记为可选
{
CACHE_VIEWS: Env.schema.boolean.optionalWhen(() => process.env.NODE_ENV === 'production')
}schema.number
schema.number 方法确保值是有效的数字。数字值的字符串表示形式会被转换为数字数据类型。
{
PORT: Env.schema.number()
}
// 标记为可选
{
PORT: Env.schema.number.optional()
}
// 按条件标记为可选
{
PORT: Env.schema.number.optionalWhen(() => process.env.NODE_ENV === 'production')
}schema.enum
schema.enum 方法针对预定义值之一验证环境变量。枚举选项可以指定为值的数组或 TypeScript 原生枚举类型。
{
NODE_ENV: Env
.schema
.enum(['development', 'production'] as const)
}
// 标记为可选
{
NODE_ENV: Env
.schema
.enum
.optional(['development', 'production'] as const)
}
// 按条件标记为可选
{
NODE_ENV: Env
.schema
.enum
.optionalWhen(
() => process.env.NODE_ENV === 'production',
['development', 'production'] as const
)
}
// 使用原生枚举
enum NODE_ENV {
development = 'development',
production = 'production'
}
{
NODE_ENV: Env.schema.enum(NODE_ENV)
}自定义函数
自定义函数可以执行模式 API 未涵盖的验证。
函数接收环境变量的名称作为第一个参数,值作为第二个参数。它必须在验证后返回最终值。
{
PORT: (name, value) => {
if (!value) {
throw new Error('Value for PORT is required')
}
if (isNaN(Number(value))) {
throw new Error('Value for PORT must be a valid number')
}
return Number(value)
}
}定义环境变量
在开发中
在开发期间,环境变量定义在 .env 文件中。env 模块在项目根目录中查找此文件并自动解析它(如果存在)。
// title: .env
PORT=3333
HOST=0.0.0.0
NODE_ENV=development
APP_KEY=sH2k88gojcp3PdAJiGDxof54kjtTXa3g
SESSION_DRIVER=cookie
CACHE_VIEWS=false在生产中
在生产环境中,建议使用你的部署平台来定义环境变量。大多数现代部署平台都有从其 Web UI 定义环境变量的一流支持。
假设你的部署平台没有提供定义环境变量的方法。你可以在项目根目录或生产服务器上的某个不同位置创建 .env 文件。
AdonisJS 将自动从项目根目录读取 .env 文件。但是,当 .env 文件存储在某个不同位置时,你必须设置 ENV_PATH 变量。
# 尝试从项目根目录读取 .env 文件
node server.js
# 从 "/etc/secrets" 目录读取 .env 文件
ENV_PATH=/etc/secrets node server.js在测试期间
特定于测试环境的环境变量必须定义在 .env.test 文件中。此文件中的值会覆盖 .env 文件中的值。
// title: .env
NODE_ENV=development
SESSION_DRIVER=cookie
ASSETS_DRIVER=vite// title: .env.test
NODE_ENV=test
SESSION_DRIVER=memory
ASSETS_DRIVER=fake// 在测试期间
import env from '#start/env'
env.get('SESSION_DRIVER') // memory所有其他 dot-env 文件
除了 .env 文件,AdonisJS 还会处理来自以下 dot-env 文件的环境变量。因此,如果需要,你可以选择性地创建这些文件。
排名最高的文件会覆盖排名较低文件中的值。
| 排名 | 文件名 | 说明 |
|---|---|---|
| 第1 | .env.[NODE_ENV].local | 为当前 NODE_ENV 加载。例如,如果 NODE_ENV 设置为 development,则会加载 .env.development.local 文件。 |
| 第2 | .env.local | 在除 test 和 testing 环境之外的所有环境中加载 |
| 第3 | .env.[NODE_ENV] | 为当前 NODE_ENV 加载。例如,如果 NODE_ENV 设置为 development,则会加载 .env.development 文件。 |
| 第4 | .env | 在所有环境中加载。当在其中存储密钥时,你应该将此文件添加到 .gitignore。 |
使用标识符进行插值
你可以定义和使用"标识符"来更改插值行为。标识符是一个字符串,作为环境变量值的前缀,让你自定义值解析。
import { EnvParser } from '@adonisjs/env'
EnvParser.defineIdentifier('base64', (value) => {
return Buffer.from(value, 'base64').toString()
})
const envParser = new EnvParser(`
APP_KEY=base64:U7dbSKkdb8wjVFOTq2osaDVz4djuA7BRLdoCUJEWxak=
`)
console.log(await envParser.parse())在上面的例子中,base64: 前缀告诉 env 解析器在返回值之前从 base64 解码值。
或者,你可以使用 defineIdentifierIfMissing 方法定义标识符。此方法不会覆盖现有标识符。
EnvParser.defineIdentifierIfMissing('base64', (value) => {
return Buffer.from(value, 'base64').toString()
})TIP
你可以直接在 start/env.ts 文件中使用这些方法。
// title: start/env.ts
import { Env } from '@adonisjs/core/env'
Env.defineIdentifier('base64', (value) => {
return Buffer.from(value, 'base64').toString()
})
export default await Env.create(APP_ROOT, {
APP_KEY: Env.schema.string()
})在 dot-env 文件中使用变量
在 dot-env 文件中,你可以使用变量替换语法引用其他环境变量。
在下面的例子中,我们从 HOST 和 PORT 属性计算 APP_URL。
HOST=localhost
PORT=3333
// highlight-start
URL=$HOST:$PORT
// highlight-end$ 符号后的所有字母、数字和下划线 (_) 用于形成变量名。如果名称包含下划线以外的特殊字符,你必须用花括号 {} 包装变量名。
REDIS-USER=admin
REDIS-URL=localhost@${REDIS-USER}转义 $ 符号
要将 $ 符号用作值,你必须转义它以防止变量替换。
PASSWORD=pa\$\$word