创建你的第一个插件

创建你的第一个 Better Auth 插件的逐步指南。

在本指南中,我们将带你一步步创建你的第一个 Better Auth 插件。

本指南假设你已经 完成了基础设置,并且准备创建你的第一个插件。

规划你的想法

在开始之前,你必须明确你想要创建什么样的插件。

在本指南中,我们将创建一个 生日插件,用于记录用户的出生日期。

首先创建服务端插件

Better Auth 插件以成对形式工作:一个 服务端插件 和一个 客户端插件。 服务端插件构成了你认证系统的基础,而客户端插件则提供了方便的前端 API,用于与服务端实现进行交互。

你可以在我们的 文档 中了解更多关于服务端/客户端插件的信息。

创建服务端插件

找到一个合适的位置来创建你的生日插件文件夹,并在其中创建一个 index.ts 文件。

index.ts

index.ts 文件中,我们将导出一个函数来代表我们的服务端插件。 这将是我们后续添加到 auth.ts 文件插件列表中的内容。

index.ts
import { createAuthClient } from "better-auth/client";
import type { BetterAuthPlugin } from "better-auth";

export const birthdayPlugin = () =>
  ({
    id: "birthdayPlugin",
  } satisfies BetterAuthPlugin);

虽然这并没有实现任何功能,但技术上你已经创建了自己的第一个插件,祝贺你!🎉

定义一个数据表结构

为了保存每个用户的生日数据,我们必须在 user 模型之上创建一个数据表结构。

在这里创建数据表结构,也能让 Better Auth 的 CLI 生成更新数据库所需的脚本。

你可以在这里了解更多关于 插件数据表结构

index.ts
//...
export const birthdayPlugin = () =>
  ({
    id: "birthdayPlugin",
    schema: {
      user: {
        fields: {
          birthday: {
            type: "date", // string, number, boolean, date
            required: true, // 如果该字段在新记录中为必填项。(默认:false)
            unique: false, // 如果该字段需要唯一性。(默认:false)
          },
        },
      },
    },
  } satisfies BetterAuthPlugin);

授权逻辑

在这个示例指南中,我们将设置认证逻辑来检查并确保注册用户年龄大于 5 岁。 但同样的概念也可以应用于验证用户是否同意服务条款等情况。

为此,我们将使用 钩子(Hooks),它允许我们在某个操作执行 之前之后 运行代码。

index.ts
export const birthdayPlugin = () => ({
    //...
    // 在我们的例子中,我们想编写授权逻辑,
    // 意味着我们想在操作执行 `之前` 进行拦截。
    hooks: {
      before: [
        {
          matcher: (context) => /* ... */,
          handler: createAuthMiddleware(async (ctx) => {
            //...
          }),
        },
      ],
    },
} satisfies BetterAuthPlugin)

在我们的例子中,我们希望匹配所有发送到注册路径的请求:

Before hook
{
  matcher: (context) => context.path.startsWith("/sign-up/email"),
  //...
}

对于我们的逻辑,我们将编写以下代码来检查用户的生日是否使他们年龄大于 5 岁。

导入部分
import { createAuthMiddleware, APIError } from "better-auth/api";
Before hook
{
  //...
  handler: createAuthMiddleware(async (ctx) => {
    const { birthday } = ctx.body;
    if(!(birthday instanceof Date)) {
      throw new APIError("BAD_REQUEST", { message: "生日必须为 Date 类型。" });
    }

    const today = new Date();
    const fiveYearsAgo = new Date(today.setFullYear(today.getFullYear() - 5));

    if(birthday >= fiveYearsAgo) {
      throw new APIError("BAD_REQUEST", { message: "用户必须年满 5 岁。" });
    }

    return { context: ctx };
  }),
}

已授权! 🔒

我们现在已成功编写了确保年龄大于 5 岁的授权代码。

客户端插件

我们快完成了!🏁

现在我们已经创建了服务端插件,下一步是开发客户端插件。 由于这个插件不涉及太多的前端 API,因此操作相对简单!

首先,创建我们的 client.ts 文件:

index.ts
client.ts

然后,添加以下代码:

client.ts
import type { BetterAuthClientPlugin } from "better-auth/client";
import type { birthdayPlugin } from "./index"; // 确保导入服务端插件作为类型

type BirthdayPlugin = typeof birthdayPlugin;

export const birthdayClientPlugin = () => {
  return {
    id: "birthdayPlugin",
    $InferServerPlugin: {} as ReturnType<BirthdayPlugin>,
  } satisfies BetterAuthClientPlugin;
};

我们所做的是允许客户端插件推断服务端插件中定义的数据表结构类型。

就这样!这就是生日客户端插件所需的一切。🎂

启用你的插件!

服务端和客户端插件都已准备就绪,最后一步是将它们分别导入到 auth-client.tsserver.ts 文件中进行启用。

服务端启用

server.ts
import { betterAuth } from "better-auth";
import { birthdayPlugin } from "./birthday-plugin";
 
export const auth = betterAuth({
    plugins: [
      birthdayPlugin(),
    ]
});

客户端启用

auth-client.ts
import { createAuthClient } from "better-auth/client";
import { birthdayClientPlugin } from "./birthday-plugin/client";
 
const authClient = createAuthClient({
    plugins: [
      birthdayClientPlugin()
    ]
});

别忘了,数据表结构!

不要忘记将你的 birthday 字段添加到 user 表模型中!

或者,使用 generate CLI 命令

npx auth@latest generate

总结

恭喜!你已成功创建了你的第一个 Better Auth 插件。 我们强烈建议你访问我们的 插件文档 以了解更多信息。

如果你有一个想要与社区分享的插件,请随时通过 我们的 Discord 服务器 或通过 拉取请求 与我们联系,我们可能会将其添加到 社区插件列表