# 数据库 (/docs/concepts/database)

了解数据库适配器、迁移、使用 Redis 的二级存储、核心架构（用户、会话、账户、验证）、自定义表、扩展架构、ID 生成、数据库钩子和插件架构。



## 适配器 [#适配器]

Better Auth 连接数据库以存储数据。数据库将用于存储用户、会话等数据。插件也可以定义自己的数据库表以存储数据。

你可以通过在数据库选项中传入支持的数据库实例，将数据库连接传递给 Better Auth。你可以在 [其他关系型数据库](/docs/adapters/other-relational-databases) 文档中了解更多支持的数据库适配器。

<Callout type="info">
  Better Auth 也可以在没有任何数据库的情况下工作。更多详情请参见 [无状态会话管理](/docs/concepts/session-management#无状态会话管理)。
</Callout>

## 命令行工具（CLI） [#命令行工具cli]

Better Auth 配备了 CLI 工具，用于管理数据库迁移和生成架构。

### 运行迁移 [#运行迁移]

CLI 会检查你的数据库，并提示你添加缺失的表或用新列更新现有表。这仅支持内置的 Kysely 适配器。对于其他适配器，可以使用 `generate` 命令生成架构，并通过你的 ORM 处理迁移。

<CodeBlockTabs defaultValue="npm" groupId="persist-install">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx auth@latest migrate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx auth@latest migrate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx auth@latest migrate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x auth@latest migrate
    ```
  </CodeBlockTab>
</CodeBlockTabs>

<Callout type="info">
  对于 PostgreSQL 用户：migrate 命令支持非默认 schema。它会自动检测你的 `search_path` 配置，并在正确的 schema 中创建表。详情请参见 [PostgreSQL 适配器](/docs/adapters/postgresql#使用非默认模式)。
</Callout>

### 生成架构 [#生成架构]

Better Auth 还提供了用于生成 Better Auth 所需架构的 `generate` 命令。如果你使用 Prisma 或 Drizzle 等数据库适配器，此命令会为你的 ORM 生成正确的架构。如果你使用内置的 Kysely 适配器，它会生成一个可以直接在数据库上运行的 SQL 文件。

<CodeBlockTabs defaultValue="npm" groupId="persist-install">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npx auth@latest generate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm dlx auth@latest generate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn dlx auth@latest generate
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun x auth@latest generate
    ```
  </CodeBlockTab>
</CodeBlockTabs>

更多 CLI 信息，请参阅 [CLI](/docs/concepts/cli) 文档。

<Callout>
  如果你更喜欢手动添加表也可以。Better Auth 所需的核心架构如下所述，插件文档中可以找到插件所需的额外架构。
</Callout>

### 编程式迁移 [#编程式迁移]

在无法使用 CLI 的环境中（例如 Cloudflare Workers，无服务器函数），你可以使用 `better-auth/db/migration` 中的 `getMigrations` 编程运行迁移。

```typescript
import { getMigrations } from "better-auth/db/migration";
import { auth } from "./auth";

const { toBeCreated, toBeAdded, runMigrations } = await getMigrations(auth.options);

await runMigrations();
```

<Callout type="warn">
  `getMigrations` 仅适用于内置的 Kysely 适配器（SQLite/D1、PostgreSQL、MySQL、MSSQL）。它**不**适用于 Prisma 或 Drizzle ORM 适配器——请改用这些 ORM 的 CLI 迁移。
</Callout>

<Accordions>
  <Accordion id="cloudflare-d1-example" title="示例：Cloudflare D1">
    [Cloudflare D1](https://developers.cloudflare.com/d1/) 只能通过 Cloudflare Worker 查询，因此 CLI 无法直接访问它。相反，你可以通过一个端点来运行编程式迁移：

    ```typescript title="auth.ts"
    import { env } from "cloudflare:workers";
    import { betterAuth } from "better-auth";

    export const auth = betterAuth({
      database: env.DB,
      // ... 其余配置
    });
    ```

    ```typescript title="src/index.ts"
    import { Hono } from "hono";
    import { auth } from "./auth";
    import { getMigrations } from "better-auth/db/migration";

    const app = new Hono();

    // 在生产环境中保护或移除此端点
    app.post("/migrate", async (c) => {
      try {
        const { toBeCreated, toBeAdded, runMigrations } = await getMigrations(auth.options);

        if (toBeCreated.length === 0 && toBeAdded.length === 0) {
          return c.json({ message: "无需迁移" });
        }

        await runMigrations();
        return c.json({
          message: "迁移成功完成",
          created: toBeCreated.map((t) => t.table),
          added: toBeAdded.map((t) => t.table),
        });
      } catch (error) {
        return c.json(
          { error: error instanceof Error ? error.message : "迁移失败" },
          500,
        );
      }
    });

    app.on(["POST", "GET"], "/api/auth/*", (c) => {
      return auth.handler(c.req.raw);
    });

    export default app;
    ```

    <Callout type="info">
      如果你在使用 Cloudflare D1 并搭配 Drizzle 或 Prisma，请使用 [`cloudflare:workers`](https://developers.cloudflare.com/workers/runtime-apis/bindings/) 来访问 `env`，并遵循以下指南：

      * [Cloudflare D1 搭配 Drizzle 指南](https://orm.drizzle.team/docs/guides/d1-http-with-drizzle-kit)
      * [Cloudflare D1 搭配 Prisma 指南](https://www.prisma.io/docs/guides/cloudflare-d1)
    </Callout>
  </Accordion>
</Accordions>

## 架构验证 [#架构验证]

初始化期间，Better Auth 会将架构与其写入数据的表进行比较，并报告缺失的表、缺失的列，以及它从未填充的必需列，同时提供相应的修复方法。错误会通过你配置的日志记录器显示，无需等待身份验证请求。请求会等待同一项检查；如果架构不匹配，请求将失败。验证不会自动停止服务器或构建。每当你想显式检查架构而不发出身份验证请求时，都可以运行 [`npx auth@latest check schema`](/docs/concepts/cli#架构)。

默认情况下会启用验证，包括在生产环境中；每个适配器实例都会缓存通过检查的结果或不匹配情况。编程式迁移会使同一数据库实例的缓存检查失效。等待检查结果的请求会等待新的检查结果。使用其他工具应用架构更改后，请重启。Kysely 会读取实时数据库元数据，因此初始化期间需要访问数据库。Drizzle 会检查配置的架构对象，Prisma 会检查生成的客户端数据模型，两者都不会查询数据库。这些本地检查无法检测尚未应用到数据库的迁移。精简的 `prisma-client` 模型不包含可空性信息，因此 `auth generate` 会报告未写入的必需列。没有已注册检查的自定义适配器不会进行验证。

对于不支持架构验证的适配器，如果你显式设置 `validateSchema: true`，初始化时会记录一条警告；如果省略此选项，则会记录一条调试消息。数据库操作会照常进行。

将 `advanced.database.validateSchema` 设置为 `false`，即可禁用运行时验证及其跳过提示。`auth migrate` 和 `auth generate` 仍会保留各自的架构诊断功能。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
	advanced: {
		database: {
			validateSchema: false, // [!code highlight]
		},
	},
});
```

## 二级存储 [#二级存储]

Better Auth 的辅助存储允许您使用键值存储来管理会话数据、验证记录、限流计数器以及其他短暂的认证数据。当您希望将高强度记录的存储卸载到高性能存储甚至内存时，这会非常有用。

### 实现 [#实现]

要使用二级存储，实现 `SecondaryStorage` 接口：

```typescript
interface SecondaryStorage {
  get: (key: string) => Promise<unknown>;
  getAndDelete: (key: string) => Promise<unknown>;
  increment: (key: string, ttl: number) => Promise<number>;
  set: (key: string, value: string, ttl?: number) => Promise<void>;
  delete: (key: string) => Promise<void>;
}
```

然后，将你的实现传递给 `betterAuth` 函数：

```typescript title="auth.ts"
import { betterAuth } from "better-auth";

betterAuth({
  // ... 其他选项
  secondaryStorage: {
    // 你的实现
  },
});
```

### Redis 存储 [#redis-存储]

对于大多数应用程序，我们建议使用官方的 Redis 存储包，该包使用 [ioredis](https://github.com/redis/ioredis)。

#### 官方 Redis 存储 [#官方-redis-存储]

<CodeBlockTabs defaultValue="npm" groupId="persist-install">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm install @better-auth/redis-storage ioredis
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add @better-auth/redis-storage ioredis
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add @better-auth/redis-storage ioredis
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add @better-auth/redis-storage ioredis
    ```
  </CodeBlockTab>
</CodeBlockTabs>

```typescript title="auth.ts"
import { betterAuth } from "better-auth";
import { Redis } from "ioredis";
import { redisStorage } from "@better-auth/redis-storage";

const redis = new Redis(process.env.REDIS_URL!);

export const auth = betterAuth({
	// ... 其他选项
	secondaryStorage: redisStorage({
		client: redis,
		keyPrefix: "better-auth:", // 可选，默认为 "better-auth:"
	}),
});
```

#### 自定义实现 [#自定义实现]

<Tabs items="[&#x22;node-redis&#x22;, &#x22;Upstash Redis&#x22;]">
  <Tab value="node-redis">
    如果你使用的是 Redis 7 或更高版本，可以使用
    [node-redis](https://github.com/redis/node-redis) 实现二级存储：

    <CodeBlockTabs defaultValue="npm" groupId="persist-install">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm install redis
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add redis
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add redis
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add redis
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ```typescript title="auth.ts"
    import { betterAuth, type SecondaryStorage } from "better-auth";
    import { createClient } from "redis";

    const redis = createClient();
    await redis.connect();

    const redisSecondaryStorage: SecondaryStorage = {
      get(key) {
        return redis.get(key);
      },

      getAndDelete(key) {
        return redis.getDel(key);
      },

      async increment(key, ttl) {
        if (!Number.isInteger(ttl) || ttl <= 0) {
          throw new TypeError("Redis increment TTL must be a positive integer");
        }

        const [value] = await redis
          .multi()
          .incr(key)
          .expire(key, ttl, "NX")
          .execTyped();

        return value;
      },

      async set(key, value, ttl) {
        if (ttl) await redis.set(key, value, { EX: ttl });
        else await redis.set(key, value);
      },

      async delete(key) {
        await redis.del(key);
      },
    };

    export const auth = betterAuth({
      // ... other options
      secondaryStorage: redisSecondaryStorage,
    });
    ```
  </Tab>

  <Tab value="Upstash Redis">
    安装 Upstash Redis 客户端：

    <CodeBlockTabs defaultValue="npm" groupId="persist-install">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm install @upstash/redis
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add @upstash/redis
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add @upstash/redis
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add @upstash/redis
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    在环境中设置 `UPSTASH_REDIS_REST_URL` 和 `UPSTASH_REDIS_REST_TOKEN`。
    有关其他配置选项，请参阅 [Upstash Redis TypeScript SDK 指南](https://upstash.com/docs/redis/sdks/ts/getstarted)。

    ```typescript title="auth.ts"
    import { Redis } from "@upstash/redis";
    import { betterAuth, type SecondaryStorage } from "better-auth";

    const redis = Redis.fromEnv();

    const redisSecondaryStorage: SecondaryStorage = {
      get(key) {
        return redis.get(key);
      },

      getAndDelete(key) {
        return redis.getdel(key);
      },

      async increment(key, ttl) {
        if (!Number.isInteger(ttl) || ttl <= 0) {
          throw new TypeError("Redis increment TTL must be a positive integer");
        }

        const [value] = await redis
          .multi()
          .incr(key)
          .expire(key, ttl, "NX")
          .exec();

        return value;
      },

      async set(key, value, ttl) {
        if (ttl) {
          await redis.set(key, value, { ex: ttl });
        } else {
          await redis.set(key, value);
        }
      },

      async delete(key) {
        await redis.del(key);
      },
    };

    export const auth = betterAuth({
      // ... other options
      secondaryStorage: redisSecondaryStorage,
    });
    ```
  </Tab>
</Tabs>

直接实现二级存储时，请为其键添加前缀，以避免与共享同一 Redis 数据库的其他应用程序发生冲突。

## 核心架构 [#核心架构]

Better Auth 需要数据库中存在以下表。下面是 `typescript` 格式的类型定义。你可以在数据库中使用对应类型。

### 用户表 [#用户表]

表名：`user`。



<DatabaseTable name="user" fields="userTableFields" />

### 会话表 [#会话表]

表名：`session`



<DatabaseTable name="session" fields="sessionTableFields" />

### 账户表 [#账户表]

表名：`account`

账户表示与用户关联的一种身份验证方式。Better Auth 通过 `providerId` 和 `accountId` 这一对值识别提供方侧的身份，而 `id` 用于标识本地账户行。当账户 API 请求 `accountId` 时，请使用 `id`。



<DatabaseTable name="account" fields="accountTableFields" />

凭据账户使用 `credential` provider ID，并将关联用户的稳定 `id` 用作 `accountId`。

### 验证 [#验证]

表名：`verification`



<DatabaseTable name="verification" fields="verificationTableFields" />

## 自定义表 [#自定义表]

Better Auth 允许您自定义核心架构的表名和列名。您还可以通过向用户表和会话表添加额外字段来扩展核心架构。

### 自定义表名 [#自定义表名]

您可以通过在身份验证配置中使用 `modelName` 和 `fields` 属性来自定义核心架构的表名和列名：

```ts title="auth.ts"
export const auth = betterAuth({
  user: {
    modelName: "users",
    fields: {
      name: "full_name",
      email: "email_address",
    },
  },
  session: {
    modelName: "user_sessions",
    fields: {
      userId: "user_id",
    },
  },
});
```

<Callout>
  代码中的类型推断仍然使用原始字段名（例如 `user.name`，而不是 `user.full_name`）。
</Callout>

对于插件特定的表和列名，您可以在插件配置中使用 `schema` 属性：

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    twoFactor({
      schema: {
        user: {
          fields: {
            twoFactorEnabled: "two_factor_enabled",
            secret: "two_factor_secret",
          },
        },
      },
    }),
  ],
});
```

### 扩展核心架构 [#扩展核心架构]

Better Auth 提供了类型安全的方式来扩展 `user` 和 `session` 架构。您可以在身份验证配置中添加自定义字段，CLI 将自动更新数据库架构。这些额外字段将在接受用户或会话对象的函数中被正确推断，例如 `useSession` 和 `signUp.email`。

要添加自定义字段，请在身份验证配置的 `user` 或 `session` 对象中使用 `additionalFields` 属性。`additionalFields` 对象使用字段名作为键，值是一个包含以下属性的 `FieldAttributes` 对象：

* `type`：字段的数据类型（例如 `"string"`、`"number"`、`"boolean"`）。
* `required`：一个布尔值，表示该字段是否为必填项。
* `defaultValue`：字段的默认值（注意：这仅适用于 JavaScript 层；在数据库中，该字段仍为可选字段）。
* `input`：Better Auth 在创建或更新记录时是否接受该字段（默认为 `true`）。对于用户字段，这包括来自 API 输入和 `mapProfileToUser` 的值。对于 `role` 等由服务器管理的字段，请将其设置为 `false`。
* `returned`：Better Auth 是否在响应正文中包含存储的字段（默认为 `true`）。这不会影响输入是否可以写入该字段。

以下是将用户架构扩展为包含额外字段的示例：

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  user: {
    additionalFields: {
      role: {
        type: ["user", "admin"],
        required: false,
        defaultValue: "user",
        input: false, // 不允许用户设置角色
      },
      lang: {
        type: "string",
        required: false,
        defaultValue: "en",
      },
    },
  },
});
```

这使您可以在应用程序逻辑中访问这些额外字段。

```ts
// 注册期间
const res = await auth.api.signUpEmail({
	body: {
		email: 'test@example.com',
		password: 'password',
		name: 'John Doe',
		lang: 'fr',
	},
});

// 用户对象
res.user.role; // > "user"
res.user.lang; // > "fr"
```

<Callout>
  有关如何在客户端推断额外字段信息的更多详细信息，请参阅 [TypeScript](/docs/concepts/typescript#在客户端推断额外字段) 文档。
</Callout>

对于 `user.additionalFields`，`input` 和 `returned` 是相互独立的：

| 配置                                  | 输入行为                                                                      | 响应行为     |
| ----------------------------------- | ------------------------------------------------------------------------- | -------- |
| `{ input: true, returned: true }`   | API 输入和提供商配置文件映射可以提供该字段。                                                  | 包含存储的字段。 |
| `{ input: true, returned: false }`  | API 输入和提供商配置文件映射可以提供该字段。                                                  | 忽略存储的字段。 |
| `{ input: false, returned: true }`  | API 输入和提供商配置文件映射无法提供该字段。配置的 `defaultValue` 可以对其进行初始化；否则请使用应用程序管理的数据库写入操作。 | 包含存储的字段。 |
| `{ input: false, returned: false }` | API 输入和提供商配置文件映射无法提供该字段。配置的 `defaultValue` 可以对其进行初始化；否则请使用应用程序管理的数据库写入操作。 | 忽略存储的字段。 |

如果您使用社交登录或 OAuth 提供商，`mapProfileToUser` 可以在这些字段允许输入时，从提供商配置文件中填充额外字段。映射器在您的服务器上运行，但 Better Auth 仍会将其返回值视为提供商输入。标记为 `input: false` 的字段仍由服务器管理，提供商为这些字段提供的值会被忽略，并且当 OAuth 创建用户时，配置的 `defaultValue` 值仍会生效。有关安全处理模式，请参阅[服务器管理的字段和授权声明](/docs/concepts/oauth#服务器拥有的字段与授权声明)。

**将配置映射到用户的示例：映射 `firstName` 和 `lastName`**

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: "YOUR_GITHUB_CLIENT_ID",
      clientSecret: "YOUR_GITHUB_CLIENT_SECRET",
      mapProfileToUser: (profile) => {
        return {
          firstName: profile.name.split(" ")[0],
          lastName: profile.name.split(" ")[1],
        };
      },
    },
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      mapProfileToUser: (profile) => {
        return {
          firstName: profile.given_name,
          lastName: profile.family_name,
        };
      },
    },
  },
});
```

### ID 生成 [#id-生成]

默认情况下，Better Auth 为用户、会话和其他实体生成唯一 ID。
您可以通过 `advanced.database.generateId` 选项自定义 ID 生成行为。

#### 选项 1：使用数据库生成 ID [#选项-1使用数据库生成-id]

将 `generateId` 设置为 `false`，让数据库处理所有 ID 生成（除了 `generateId` 为 `"serial"` 和某些 `uuid` 的情况）：

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { db } from "./db";

export const auth = betterAuth({
  database: db,
  advanced: {
    database: {
      generateId: false, // 如果您想要自动递增的数字 ID，请使用 "serial"
    },
  },
});
```

#### 选项 2：自定义 ID 生成函数 [#选项-2自定义-id-生成函数]

使用函数生成 ID。返回 `false` 或 `undefined` 告诉数据库为特定模型生成 ID：

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { db } from "./db";

export const auth = betterAuth({
  database: db,
  advanced: {
    database: {
      generateId: (options) => {
        // 让数据库为特定模型生成 ID
        if (options.model === "user" || options.model === "users") {
          return false; // 数据库生成 ID
        }
        // 其他表生成 UUID
        return crypto.randomUUID();
      },
    },
  },
});
```

<Callout type="info">
  **重要**：当 `generateId` 函数返回 `false` 或 `undefined` 时，对应模型的 ID 由数据库生成。直接将 `generateId: false`（而非函数）设置为 `false` 会禁用**所有**表的 ID 生成。
</Callout>

#### 选项 3：统一的 ID 生成器 [#选项-3统一的-id-生成器]

为所有表生成相同类型的 ID：

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { db } from "./db";

export const auth = betterAuth({
  database: db,
  advanced: {
    database: {
      generateId: () => crypto.randomUUID(),
    },
  },
});
```

### 数字 ID [#数字-id]

如果您更喜欢自动递增的数字 ID，请将 `advanced.database.generateId` 设置为 `"serial"`。
在这种情况下，Better Auth 不会生成 ID，而是假设数据库会自动生成数字 ID。

启用后，Better Auth CLI 将生成或迁移数据库的 `id` 字段为具有自动递增属性的数字类型。

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { db } from "./db";

export const auth = betterAuth({
  database: db,
  advanced: {
    database: {
      generateId: "serial",
    },
  },
});
```

<Callout type="info">
  Better Auth 仍将数据库中的 `id` 字段视为字符串类型，并在检索或插入数据时自动转换为数字类型。

  从 Better Auth 获取的 `id` 可能是表示数字的字符串形式，这是正常的。您传递给 Better Auth 的 `id` 也应该是字符串。
</Callout>

### UUID [#uuid]

如果您希望使用 UUID 类型的 `id` 字段，请将 `advanced.database.generateId` 设置为 `"uuid"`。
默认情况下，Better Auth 为所有表生成 UUID，除了 PostgreSQL 适配器（它支持数据库生成的 UUID）。

启用后，Better Auth CLI 将生成或迁移您的数据库架构，并为 `id` 字段使用 UUID 类型。
如果数据库不支持 UUID 类型，它会回退到纯字符串类型。

### 混合 ID 类型 [#混合-id-类型]

如果您需要在不同表中使用不同的 ID 类型（例如，用户表使用整数，其他表使用 UUID 字符串），请使用 `generateId` 回调函数方法。

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { db } from "./db";

export const auth = betterAuth({
  database: db,
  user: {
    modelName: "users", // PostgreSQL：id 是 serial 主键
  },
  session: {
    modelName: "session", // PostgreSQL：id 是 text 主键
  },
  advanced: {
    database: {
      // 不要设置 useNumberId —— 这个选项是全局的，会影响所有表
      generateId: (options) => {
        if (options.model === "user" || options.model === "users") {
          return false; // 让 PostgreSQL serial 生成 ID
        }
        return crypto.randomUUID(); // session、account、verification 生成 UUID
      },
    },
  },
});
```

此配置允许您：

* 使用数据库自动递增（serial、auto\_increment 等）作为用户表
* 为所有其他表（session、account、verification）生成 UUID
* 与使用不同 ID 类型的现有架构保持兼容

<Callout type="info">
  **使用场景**：这在从另一个身份验证提供程序（例如 Clerk）迁移时特别有用，其中您的用户 ID 可能是整数，而新表使用 UUID 字符串。
</Callout>

### 数据库钩子 [#数据库钩子]

数据库钩子允许您定义在 Better Auth 核心数据库操作生命周期期间运行的自定义逻辑。您可以为以下模型创建钩子：**user**、**session** 和 **account**。

<Callout type="warn">
  支持额外字段，但这些字段的完整类型推断尚未支持，未来将会改进。
</Callout>

您可以定义两种类型的钩子：

#### 1. Before 钩子 [#1-before-钩子]

* **目的**：在创建、更新或删除相应实体（用户、会话或账户）之前调用此钩子。
* **行为**：如果钩子返回 `false`，操作将被中止；如果返回数据对象，该对象会替换原始有效载荷。使用 MongoDB 事务时，同一操作的 `before` 钩子可能运行多次：如果 MongoDB 因并发写入而中止事务，适配器会重试操作。应改用 `after` 钩子发送邮件或调用其他服务。

#### 2. After 钩子 [#2-after-钩子]

* **目的**：在相应实体创建或更新后调用此钩子。
* **行为**：实体成功创建或更新后，可以执行额外操作或修改。操作属于事务时，钩子会在事务提交后运行；如果事务回滚，则会跳过。数据库适配器未启用事务时，即使同一请求的后续步骤失败，此钩子仍会运行，因为数据库变更已经应用。

**示例用法**

```typescript title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  databaseHooks: {
    user: {
      create: {
        before: async (user, ctx) => {
          // 在创建之前修改用户对象
          return {
            data: {
              // 注意：返回 Better Auth 命名字段，而不是原始数据库字段名
              ...user,
              firstName: user.name.split(" ")[0],
              lastName: user.name.split(" ")[1],
            },
          };
        },
        after: async (user) => {
          // 执行额外操作，例如创建 Stripe 客户
        },
      },
      delete: {
        before: async (user, ctx) => {
          console.log(`用户 ${user.email} 正在被删除`);
          if (user.email.includes("admin")) {
            return false; // 中止删除
          }
          return true; // 允许删除
        },
        after: async (user) => {
          console.log(`用户 ${user.email} 已被删除`);
        },
      },
    },
    session: {
      delete: {
        before: async (session, ctx) => {
          console.log(`会话 ${session.token} 正在被删除`);
          if (session.userId === "admin-user-id") {
            return false; // 中止删除
          }
          return true; // 允许删除
        },
        after: async (session) => {
          console.log(`会话 ${session.token} 已被删除`);
        },
      },
    },
  },
});
```

#### 抛出错误 [#抛出错误]

如果您想要在数据库钩子中停止执行，可以抛出使用 `better-auth/api` 导入的 `APIError` 类：

```typescript title="auth.ts"
import { betterAuth } from "better-auth";
import { APIError } from "better-auth/api";

export const auth = betterAuth({
  databaseHooks: {
    user: {
      create: {
        before: async (user, ctx) => {
          if (user.isAgreedToTerms === false) {
            // 您的自定义条件
            // 抛出 API 错误
            throw new APIError("BAD_REQUEST", {
              message: "用户必须同意服务条款后才能注册。",
            });
          }
          return {
            data: user,
          };
        },
      },
    },
  },
});
```

#### 使用上下文对象 [#使用上下文对象]

上下文对象（`ctx`）作为第二个参数传递给钩子，并包含有用的信息。对于 `update` 钩子，`ctx` 包含当前的 `session`，允许访问经过身份验证的用户的详细信息。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  databaseHooks: {
    user: {
      update: {
        before: async (data, ctx) => {
          // 通过 ctx 访问会话
          if (ctx.context.session) {
            console.log("用户更新由以下对象发起：", ctx.context.session.userId);
          }
          return { data };
        },
      },
    },
  },
});
```

数据库钩子与标准钩子一样，也提供丰富的 `ctx` 对象属性。有关详细信息，请参阅 [hooks 文档](/docs/concepts/hooks#ctx)。

## 插件 Schema [#插件-schema]

插件可以在数据库中定义自己的表来存储更多数据，也可以向核心表中添加字段以存储额外信息。例如，双因素认证插件会向 `user` 表添加以下字段：

* `twoFactorEnabled`：该用户是否启用了双因素认证。
* `twoFactorSecret`：用于生成 TOTP 码的密钥。
* `twoFactorBackupCodes`：用于账户恢复的加密备份码。

向数据库添加表和列有两种选择：

* **CLI**：使用迁移或生成命令。该命令会扫描数据库，并引导你添加缺失的表或列。
* **手动方法**：按照插件文档手动添加表和列。

## 实验性关联查询 [#实验性关联查询]

## 连接 [#连接]

自 Better-Auth `1.4` 版本起，我们引入了数据库连接支持。
这使 Better-Auth 能够在单个请求中执行多个数据库查询，从而减少数据库往返次数。
目前已有超过 50 个端点支持连接，我们也在不断添加更多端点。

在底层，我们的适配器系统原生支持连接。禁用连接时（默认设置），
Better-Auth 会回退到执行多个数据库查询并合并结果。

要启用连接，请使用以下配置更新您的身份验证配置：

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  advanced: {
    database: {
      joins: true,
    },
  },
});
```

请确保您的 DrizzleORM 或 PrismaORM 架构包含必要的关系——运行我们的迁移或生成 CLI 命令以保持最新状态。

请阅读与您所用适配器相关的连接文档：

* [DrizzleORM](/docs/adapters/drizzle#联接)
* [PrismaORM](/docs/adapters/prisma#连接)
* [SQLite](/docs/adapters/sqlite#联接)
* [MySQL](/docs/adapters/mysql#连接)
* [PostgreSQL](/docs/adapters/postgresql#连接)
* [MSSQL](/docs/adapters/mssql#连接)
* [MongoDB](/docs/adapters/mongo#联接)

