配置与环境

本指南涵盖 AdonisJS 应用中的配置。你将学习到:

  • config 目录中的配置文件
  • 环境变量与 .env 文件
  • 以类型安全的方式验证环境变量
  • .env 文件内的变量插值
  • 用于开发、测试和生产环境的环境特定 .env 文件
  • 在 Edge 模板中访问配置
  • 用于框架配置的 adonisrc.ts 文件

概述

AdonisJS 中的配置被组织成三个不同的系统,各自服务于特定目的。

  • 配置文件 包含你的应用设置。这些文件位于 config 目录中,定义数据库连接、邮件设置、会话配置等内容。

  • 环境变量 存储在 .env 文件中,保存着在不同环境之间变化的运行时密钥与值。API 密钥、数据库密码以及环境特定的 URL 都属于此类。AdonisJS 支持用于不同环境的多个 .env 文件,并提供类型安全验证,以便在启动时捕获缺失的变量。

  • adonisrc.ts 文件 配置框架本身。它告知 AdonisJS 你的工作区是如何组织的、要加载哪些提供者以及有哪些命令可用。

配置文件

配置文件位于项目根目录下的 config 目录中。每个文件导出一个针对应用特定部分的配置对象(数据库连接、邮件设置、身份验证、会话处理等)。

一个典型的 AdonisJS 项目开箱即包含多个配置文件。config/database.ts 文件配置数据库连接,config/mail.ts 处理邮件发送,config/auth.ts 定义身份验证设置。

下面是一个数据库配置文件示例。

config/database.ts
import app from '@adonisjs/core/services/app'
import { defineConfig } from '@adonisjs/lucid'

const dbConfig = defineConfig({
  connection: 'sqlite',
  prettyPrintDebugQueries: true,
  connections: {
    sqlite: {
      client: 'better-sqlite3',
      connection: {
        filename: app.tmpPath('db.sqlite3'),
      },
      useNullAsDefault: true,
      migrations: {
        naturalSort: true,
        paths: ['database/migrations'],
      },
      debug: app.inDev,
    },
  },
})

export default dbConfig

邮件配置遵循类似的模式。

config/mail.ts
import env from '#start/env'
import { defineConfig, transports } from '@adonisjs/mail'

const mailConfig = defineConfig({
  default: env.get('MAIL_MAILER'),

  from: {
    address: env.get('MAIL_FROM_ADDRESS'),
    name: env.get('MAIL_FROM_NAME'),
  },

  mailers: {
    resend: transports.resend({
      key: env.get('RESEND_API_KEY'),
      baseUrl: 'https://api.resend.com',
    }),
  },
})

export default mailConfig

declare module '@adonisjs/mail/types' {
  export interface MailersList extends InferMailers<typeof mailConfig> {}
}

注意这个配置文件如何通过 env.get() 引用环境变量。这是在配置中使用环境特定值的正确方式。配置文件定义结构与默认值,而 .env 文件提供实际的值。

配置文件何时被加载

配置文件在应用启动周期中、在你的路由和控制器就绪之前被加载。这意味着你应该保持配置文件简单,避免导入模型、服务或控制器等应用级别的代码。

配置文件只应导入框架工具、定义配置对象并引用环境变量。导入应用代码会造成循环依赖,并导致你的应用在启动时失败

在 Edge 模板中访问配置

Edge 模板可以通过 config 全局对象访问你的应用配置。这让你能够直接在视图中引用配置值,而无需从控制器显式传递它们。

resources/views/layouts/main.edge
<!DOCTYPE html>
<html>
<head>
  <title>{{ config('app.appName') }}</title>
</head>
<body>
  <footer>
    <p>Running in {{ config('app.nodeEnv') }} mode</p>
  </footer>
</body>
</html>

config() 辅助函数接受指向任意配置值的点号路径。该路径对应配置文件名及其内部的属性。例如,config('database.connection') 读取 config/database.ts 中的 connection 属性。

你也可以提供第二个参数作为默认值。

<p>{{ config('app.timezone', 'UTC') }}</p>

环境变量

环境变量存储在不同环境之间变化的密钥与配置。在开发过程中,你在 .env 文件中定义这些变量。在生产环境中,你必须通过托管提供商的 UI 或配置界面来定义它们。

一个典型的 .env 文件如下所示。

.env
HOST=0.0.0.0
PORT=3333
APP_KEY=your-secret-app-key-here
MAIL_MAILER=resend
MAIL_FROM_ADDRESS=[email protected]
MAIL_FROM_NAME=My App
RESEND_API_KEY=re_your_api_key_here

在 AdonisJS 入门套件中,.env 文件已经列在 .gitignore 中,因此你不会意外地将密钥提交到仓库。

APP_KEY

APP_KEY 是一个特殊的环境变量,AdonisJS 使用它来加密 cookie、签名会话以及其他加密操作。每个 AdonisJS 应用都需要一个 APP_KEY 才能安全运行。

运行 generate:key 命令来创建你的 APP_KEY。

node ace generate:key

这会创建一个密码学安全的随机密钥,并自动添加到你的 .env 文件中。

APP_KEY 必须保持机密。任何获得此密钥的人都能解密你应用的加密数据并伪造会话令牌。当你部署到生产环境时,每个环境(开发、预发布、生产)都应使用不同的 APP_KEY。切勿跨环境复用密钥。

如果你的 APP_KEY 被泄露,请立即生成一个新的。这将使所有现有的用户会话和加密数据失效。

在配置文件中使用环境变量

配置文件通过 env 服务访问环境变量,该服务为你的 .env 文件值提供类型安全的访问。你导入 env 服务并使用变量名调用 env.get()

import env from '#start/env'

const apiKey = env.get('RESEND_API_KEY')

这种模式使你的配置保持有条理且经过验证。env 服务确保必需的变量存在,并在缺失时抛出清晰的错误。

你绝不应在控制器、服务或其他应用代码中直接访问环境变量。始终通过配置文件访问它们。这创建了一处配置的唯一真实来源。

变量插值

.env 文件支持变量插值,允许你在一个值中引用其他环境变量。使用 $VAR${VAR} 语法进行变量插值。

.env
HOST=localhost
PORT=3333
APP_URL=http://$HOST:$PORT

在此示例中,APP_URL 解析为 http://localhost:3333。当你需要根据其他变量组合值而不重复书写时,这非常有用。

你也可以使用花括号以提高清晰度,或在变量名与其他字符相邻时使用。

.env
S3_BUCKET=my-app-uploads
S3_REGION=us-east-1
S3_URL=https://${S3_BUCKET}.s3.${S3_REGION}.amazonaws.com

要在值中包含字面的美元符号,请使用反斜杠对其进行转义。

.env
PRICE=\$99.99

环境特定的 .env 文件

AdonisJS 支持用于不同环境和用例的多个 .env 文件。框架按照特定顺序加载这些文件,后加载的文件会覆盖先加载的文件。

文件用途Git 状态
.env所有环境的基础环境变量忽略
.env.local本地覆盖,在所有环境中加载(测试环境除外)忽略
.env.development开发特定变量忽略
.env.staging预发布特定变量忽略
.env.production生产特定变量忽略
.env.test测试特定变量,在 NODE_ENV=test 时加载忽略
.env.example展示所需变量及占位值的模板提交

加载顺序取决于你的 NODE_ENV 值。对于 NODE_ENV=development,AdonisJS 按以下顺序加载文件:

  1. .env(基础变量)
  2. .env.development(环境特定)
  3. .env.local(本地覆盖)

对于 NODE_ENV=test,顺序为:

  1. .env(基础变量)
  2. .env.test(测试特定)

注意 .env.local 在测试期间不会被加载。这可以防止本地开发设置干扰测试运行。

.env.example 文件作为你团队的文档。它列出所有必需的环境变量及占位值,帮助新开发者设置本地环境。与其他 .env 文件不同,你应该将 .env.example 提交到版本控制中。

.env.example
HOST=0.0.0.0
PORT=3333
APP_KEY=<generate-with-node-ace-generate:key>
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
RESEND_API_KEY=<your-resend-api-key>

验证环境变量

AdonisJS 通过 start/env.ts 文件在启动时验证环境变量。这种验证确保你的应用不会在配置缺失或无效的情况下启动,将错误尽早暴露出来,而不是在运行时(更难调试)才出现。

start/env.ts 文件使用基于 schema 的验证来定义你的应用期望哪些环境变量、它们的类型以及任何约束。

start/env.ts
import { Env } from '@adonisjs/core/env'

export default await Env.create(new URL('../', import.meta.url), {
  /**
   * Variables for configuring the HTTP server
   */
  HOST: Env.schema.string({ format: 'host' }),
  PORT: Env.schema.number(),
  
  /**
   * App-specific variables
   */
  APP_KEY: Env.schema.string(),
  NODE_ENV: Env.schema.enum(['development', 'production', 'test'] as const),
  
  /**
   * Database configuration
   */
  DATABASE_URL: Env.schema.string(),
  
  /**
   * Optional variables with defaults
   */
  LOG_LEVEL: Env.schema.enum(['debug', 'info', 'warn', 'error'] as const).optional(),
  CACHE_DRIVER: Env.schema.string.optional(),
})

当你的应用启动时,AdonisJS 会读取 .env 文件并根据此 schema 验证每个变量。如果某个必需变量缺失或值无效,应用会抛出描述性错误并拒绝启动。

该 schema 提供了多种验证方法。

Env.schema.string

验证值是非空字符串。你可以通过指定以下格式之一来验证字符串格式。

// Validates the value is a valid hostname or IP
Env.schema.string({ format: 'host' })

// Validates the value is a valid URL
Env.schema.string({ format: 'url' })

// Validates the value is a valid URL without tld
Env.schema.string({ format: 'url', tld: false })

// Validates the value is a valid URL without protocol
Env.schema.string({ format: 'url', protocol: false })

// Validates the value is a valid email address
Env.schema.string({ format: 'email' })
Env.schema.number

验证并将值转换为数字

Env.schema.boolean

验证并将值转换为布尔值

Env.schema.enum

验证值是否为允许的选项之一

Optional chaining(可选链)

任何 schema 方法都可以通过链式调用 .optional() 变为可选。未设置的变量会返回 undefined

start/env.ts
import { Env } from '@adonisjs/core/env'

export default await Env.create(new URL('../', import.meta.url), {
  SENTRY_DSN: Env.schema.string.optional(),
  CACHE_DRIVER: Env.schema.string.optional()
})

验证之后,env 服务提供完整的 TypeScript 类型推断。当你调用 env.get('NODE_ENV') 时,TypeScript 会根据你的 schema 定义知道返回类型为 'development' | 'production' | 'test'

adonisrc.ts 文件

adonisrc.ts 文件配置的是框架本身,而不是你的应用。虽然配置文件定义应用的行为、.env 存储密钥,但 adonisrc.ts 告诉 AdonisJS 你的工作区是如何组织的,以及要加载哪些框架特性。

你很少需要直接修改这个文件。大多数需要更改 adonisrc.ts 的操作都由 Ace 命令自动处理。当你运行 node ace make:providernode ace make:command 时,这些命令会为你把新的提供者或命令注册到 adonisrc.ts 文件中。

下面是一个基础的 adonisrc.ts 文件包含的内容。

adonisrc.ts
import { defineConfig } from '@adonisjs/core/app'

export default defineConfig({
  commands: [
    () => import('@adonisjs/core/commands'),
    () => import('@adonisjs/lucid/commands'),
  ],

  providers: [
    () => import('@adonisjs/core/providers/app_provider'),
    () => import('@adonisjs/core/providers/hash_provider'),
    () => import('@adonisjs/core/providers/vinejs_provider'),
    () => import('@adonisjs/session/session_provider'),
    () => import('@adonisjs/lucid/database_provider'),
    () => import('@adonisjs/auth/auth_provider'),
  ],

  preloads: [
    () => import('#start/routes'),
    () => import('#start/kernel')
  ],

  hooks: {
    buildStarting: [
      () => import('@adonisjs/vite/build_hook')
    ],
  },
})

providers 数组列出了 AdonisJS 在应用启动时应加载的所有服务提供者。提供者负责搭建数据库连接、身份验证和会话处理等框架特性。

commands 数组注册来自包的 Ace 命令。你的应用 commands 目录中的命令会被自动发现,因此你只在这里列出包的命令。

hooks 对象定义了在特定生命周期事件期间运行的函数。buildStarting 钩子会在你为生产环境构建应用时运行。

你可以在 AdonisRC 文件参考指南 中了解更多可用属性。