Skip to content

环境变量

环境变量用于将密钥(如数据库密码、应用程序密钥或 API 密钥)存储在应用程序代码库之外。

此外,环境变量可用于为不同的环境提供不同的配置。例如,你可以在测试期间使用内存邮件程序,在开发期间使用 SMTP 邮件程序,在生产环境中使用第三方服务。

由于所有操作系统、部署平台和 CI/CD 管道都支持环境变量,它们已成为存储密钥和特定于环境的配置的事实标准。

在本指南中,我们将学习如何在 AdonisJS 应用程序中利用环境变量。

读取环境变量

Node.js 原生通过 process.env 全局属性 将所有环境变量作为对象公开,你可以按如下方式访问它们。

dotenv
process.env.NODE_ENV
process.env.HOST
process.env.PORT

使用 AdonisJS env 模块

通过 process.env 对象读取环境变量不需要 AdonisJS 端的任何设置,因为 Node.js 运行时支持它。但是,在本文档的其余部分,我们将使用 AdonisJS env 模块,原因如下:

  • 能够从多个 .env 文件存储和解析环境变量。
  • 在应用程序启动时立即验证环境变量。
  • 为已验证的环境变量提供静态类型安全。

env 模块在 start/env.ts 文件中实例化,你可以在应用程序的其他地方按如下方式访问它。

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 模块仅在服务器端渲染期间可用。

ts
// 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.stringschema.number)的引用。
ts
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 变体来允许空字符串。

ts
{
  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 地址。

ts
{
  HOST: Env.schema.string({ format: 'host' })
}

url

验证值是有效的 URL。可选地,你可以通过允许 URL 没有 protocoltld 来使验证不那么严格。

ts
{
  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

验证值是有效的电子邮件地址。

ts
{
  SENDER_EMAIL: Env.schema.string({ format: 'email' })
}

schema.boolean

schema.boolean 方法确保值是有效的布尔值。空值会使验证失败,你必须使用 optional 变体来允许空值。

'true''1''false''0' 的字符串表示形式会被转换为布尔数据类型。

ts
{
  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 方法确保值是有效的数字。数字值的字符串表示形式会被转换为数字数据类型。

ts
{
  PORT: Env.schema.number()
}

// 标记为可选
{
  PORT: Env.schema.number.optional()
}

// 按条件标记为可选
{
  PORT: Env.schema.number.optionalWhen(() => process.env.NODE_ENV === 'production')
}

schema.enum

schema.enum 方法针对预定义值之一验证环境变量。枚举选项可以指定为值的数组或 TypeScript 原生枚举类型。

ts
{
  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 未涵盖的验证。

函数接收环境变量的名称作为第一个参数,值作为第二个参数。它必须在验证后返回最终值。

ts
{
  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 模块在项目根目录中查找此文件并自动解析它(如果存在)。

dotenv
// 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 变量。

sh
# 尝试从项目根目录读取 .env 文件
node server.js

# 从 "/etc/secrets" 目录读取 .env 文件
ENV_PATH=/etc/secrets node server.js

在测试期间

特定于测试环境的环境变量必须定义在 .env.test 文件中。此文件中的值会覆盖 .env 文件中的值。

dotenv
// title: .env
NODE_ENV=development
SESSION_DRIVER=cookie
ASSETS_DRIVER=vite
dotenv
// title: .env.test
NODE_ENV=test
SESSION_DRIVER=memory
ASSETS_DRIVER=fake
ts
// 在测试期间
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在除 testtesting 环境之外的所有环境中加载
第3.env.[NODE_ENV] 为当前 NODE_ENV 加载。例如,如果 NODE_ENV 设置为 development,则会加载 .env.development 文件。
第4.env在所有环境中加载。当在其中存储密钥时,你应该将此文件添加到 .gitignore

使用标识符进行插值

你可以定义和使用"标识符"来更改插值行为。标识符是一个字符串,作为环境变量值的前缀,让你自定义值解析。

ts
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 方法定义标识符。此方法不会覆盖现有标识符。

ts
EnvParser.defineIdentifierIfMissing('base64', (value) => {
  return Buffer.from(value, 'base64').toString()
})

TIP

你可以直接在 start/env.ts 文件中使用这些方法。

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 文件中,你可以使用变量替换语法引用其他环境变量。

在下面的例子中,我们从 HOSTPORT 属性计算 APP_URL

dotenv
HOST=localhost
PORT=3333
// highlight-start
URL=$HOST:$PORT
// highlight-end

$ 符号后的所有字母数字下划线 (_) 用于形成变量名。如果名称包含下划线以外的特殊字符,你必须用花括号 {} 包装变量名。

dotenv
REDIS-USER=admin
REDIS-URL=localhost@${REDIS-USER}

转义 $ 符号

要将 $ 符号用作值,你必须转义它以防止变量替换。

dotenv
PASSWORD=pa\$\$word