# 钩子 (/docs/concepts/hooks)

了解如何使用前置和后置钩子自定义端点行为、修改请求和响应、处理 Cookie、抛出错误、访问认证上下文以及运行后台任务。



Better Auth 中的**钩子**允许你“钩入”生命周期并执行自定义逻辑。它们提供了一种在不编写完整插件的情况下自定义 Better Auth 行为的方式。

<Callout>
  如果你需要对端点进行自定义调整，而不是在 Better Auth 之外创建另一个端点，我们强烈建议使用钩子。
</Callout>

## 前置钩子（Before Hooks） [#前置钩子before-hooks]

**前置钩子**在端点执行*之前*运行。可以用来修改请求、预先验证数据或提前返回。

### 示例：强制邮箱域名限制 [#示例强制邮箱域名限制]

此钩子确保用户只有在邮箱后缀为 `@example.com` 时才能注册：

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

export const auth = betterAuth({
    hooks: {
        before: createAuthMiddleware(async (ctx) => {
            if (ctx.path !== "/sign-up/email") {
                return;
            }
            if (!ctx.body?.email.endsWith("@example.com")) {
                throw new APIError("BAD_REQUEST", {
                    message: "邮箱必须以 @example.com 结尾",
                });
            }
        }),
    },
});
```

### 示例：修改请求上下文 [#示例修改请求上下文]

在继续之前调整请求上下文：

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

export const auth = betterAuth({
    hooks: {
        before: createAuthMiddleware(async (ctx) => {
            if (ctx.path === "/sign-up/email") {
                return {
                    context: {
                        ...ctx,
                        body: {
                            ...ctx.body,
                            name: "John Doe",
                        },
                    }
                };
            }
        }),
    },
});
```

## 后置钩子（After Hooks） [#后置钩子after-hooks]

**后置钩子**在端点执行*之后*运行。可以用来修改响应。

### 示例：新用户注册时向你的频道发送通知 [#示例新用户注册时向你的频道发送通知]

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { createAuthMiddleware } from "better-auth/api";
import { sendMessage } from "@/lib/notification"

export const auth = betterAuth({
    hooks: {
        after: createAuthMiddleware(async (ctx) => {
            if(ctx.path.startsWith("/sign-up")){
                const newSession = ctx.context.newSession;
                if(newSession){
                    sendMessage({
                        type: "user-register",
                        name: newSession.user.name,
                    })
                }
            }
        }),
    },
});
```

### 示例：在单个钩子中处理多个端点 [#示例在单个钩子中处理多个端点]

由于 `before` 和 `after` 都只接受一次 `createAuthMiddleware` 调用，因此可以通过在同一个钩子内对 `ctx.path` 进行条件判断来处理多个端点：

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

export const auth = betterAuth({
    hooks: {
        after: createAuthMiddleware(async (ctx) => {
            if (ctx.path === "/reset-password") {
                // 密码重置后自动登录用户
            }
            if (ctx.path.startsWith("/sign-up")) {
                // 注册后发送欢迎通知
            }
            if (ctx.path === "/sign-in/email") {
                // 跟踪登录分析
            }
        }),
    },
});
```

<Callout>
  每个钩子（`before` / `after`）只接受一个中间件函数，而不是数组。要为不同端点运行逻辑，请在这个单一函数内部基于 `ctx.path` 进行分支判断。
</Callout>

## Ctx [#ctx]

调用 `createAuthMiddleware` 时会传入一个 `ctx` 对象，提供了许多有用的属性，包括：

* **路径：** `ctx.path` 用于获取当前端点路径。
* **请求体：** `ctx.body` 用于解析请求体（仅适用于 POST 请求）。
* **请求头：** `ctx.headers` 用于访问请求头。
* **请求：** `ctx.request` 用于访问请求对象（在仅服务端端点中可能不存在）。
* **查询参数：** `ctx.query` 用于访问查询参数。
* **上下文**：`ctx.context` 认证相关上下文，用于访问新创建的会话、Cookie 配置、密码哈希器、配置等。

以及更多信息。

### 请求与响应 [#请求与响应]

此工具允许你获取请求信息并从钩子中发送响应。

#### JSON 响应 [#json-响应]

使用 `ctx.json` 发送 JSON 响应：

```ts
import { createAuthMiddleware } from "better-auth/api";

const hook = createAuthMiddleware(async (ctx) => {
    return ctx.json({
        message: "Hello World",
    });
});
```

#### 重定向 [#重定向]

使用 `ctx.redirect` 重定向用户：

```ts
import { createAuthMiddleware } from "better-auth/api";

const hook = createAuthMiddleware(async (ctx) => {
    throw ctx.redirect("/sign-up/name");
});
```

#### Cookie [#cookie]

* 设置 Cookie：`ctx.setCookie` 或 `ctx.setSignedCookie`。
* 获取 Cookie：`ctx.getCookie` 或 `ctx.getSignedCookie`。

示例：

```ts
import { createAuthMiddleware } from "better-auth/api";

const hook = createAuthMiddleware(async (ctx) => {
    ctx.setCookie("my-cookie", "value");
    await ctx.setSignedCookie("my-signed-cookie", "value", ctx.context.secret, {
        maxAge: 1000,
    });

    const cookie = ctx.getCookie("my-cookie");
    const signedCookie = await ctx.getSignedCookie("my-signed-cookie", ctx.context.secret);
});
```

#### 错误处理 [#错误处理]

使用 `APIError` 抛出特定状态码和消息的错误：

```ts
import { createAuthMiddleware, APIError } from "better-auth/api";

const hook = createAuthMiddleware(async (ctx) => {
    throw new APIError("BAD_REQUEST", {
        message: "无效请求",
    });
});
```

### Context [#context]

`ctx` 对象内部包含另一个 `context` 对象，用于存储与认证相关的上下文信息，包括新创建的会话、Cookie 配置、密码哈希器等。

#### 新会话 [#新会话]

在端点执行后新创建的会话，仅存在于后置钩子中。

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

createAuthMiddleware(async (ctx) => {
    const newSession = ctx.context.newSession
});
```

#### 返回值 [#返回值]

钩子的返回值会传递给链中的下一个钩子。

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

createAuthMiddleware(async (ctx) => {
    const returned = ctx.context.returned; // 这可能是成功响应或 APIError
});
```

#### 响应头 [#响应头]

由端点和在该钩子之前运行的钩子添加的响应头。

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

createAuthMiddleware(async (ctx) => {
    const responseHeaders = ctx.context.responseHeaders;
});
```

#### 预定义认证 Cookie [#预定义认证-cookie]

访问 BetterAuth 的预定义 Cookie 属性：

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

createAuthMiddleware(async (ctx) => {
    const cookieName = ctx.context.authCookies.sessionToken.name;
});
```

#### Secret [#secret]

可通过 `ctx.context.secret` 访问你的认证实例的 `secret`。

#### 密码 [#密码]

密码对象提供了 `hash` 和 `verify` 方法：

* `ctx.context.password.hash`：用于对给定密码进行哈希处理。
* `ctx.context.password.verify`：用于验证给定的密码和哈希值。

#### 适配器（Adapter） [#适配器adapter]

适配器暴露了 Better Auth 使用的适配器方法，包括 `findOne`、`findMany`、`create`、`delete`、`update` 和 `updateMany`。通常建议使用你的 ORM 中的实际 `db` 实例，而不是该适配器。

#### Internal Adapter [#internal-adapter]

如果你想使用数据库查询并获得 `databaseHooks`、适当的 `secondaryStorage` 支持等功能，使用这些内部适配器调用会很有帮助。如果你正在执行类似内部适配器操作的查询，建议考虑使用它们。

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

你可以使用 `ctx.context.generateId` 来生成各种用途的 ID。

#### runInBackground [#runinbackground]

安排任务在响应发送后执行。适用于“触发即忘”的操作（如清理、分析、速率限制计数更新等）。你可以在 [advanced.backgroundTasks](/docs/reference/options#backgroundtasks) 中配置处理器。

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

export const auth = betterAuth({
  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      if (ctx.path.startsWith("/sign-up")) {
        const newSession = ctx.context.newSession;
        if (newSession) {
          ctx.context.runInBackground(sendAnalyticsEvent(newSession.user.id));
        }
      }
    }),
  },
});
```

#### runInBackgroundOrAwait [#runinbackgroundorawait]

如果配置了处理器，则延迟执行任务，否则等待其完成。适用于必须完成但有处理器时不阻塞的操作（例如发送邮件）。处理器配置见 [advanced.backgroundTasks](/docs/reference/options#backgroundtasks)。

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

export const auth = betterAuth({
  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      if (ctx.path.startsWith("/sign-up")) {
        const newSession = ctx.context.newSession;
        if (newSession) {
          await ctx.context.runInBackgroundOrAwait(
            sendWelcomeEmail(newSession.user)
          );
        }
      }
    }),
  },
});
```

## 可复用钩子 [#可复用钩子]

如果你需要在多个端点之间复用钩子，建议考虑创建插件。更多内容请参见 [插件文档](/docs/concepts/plugins)。

