# Convex 集成 (/docs/integrations/convex)

将 Better Auth 与 Convex 集成。



在本指南中，我们将介绍将 Better Auth 与 [Convex](https://www.convex.dev) 集成的步骤。

<Steps>
  <Step>
    ## 先决条件 [#先决条件]

    ### 创建一个 Convex 项目 [#创建一个-convex-项目]

    要将 Better Auth 与 Convex 一起使用，你需要一个 Convex 项目。如果你还没有，请运行以下命令创建一个新项目。更多详情请参阅 [Convex 文档](https://docs.convex.dev/home)。

    <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 create convex@latest

        # 选择用户认证：
        # > none
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm create convex

        # 选择用户认证：
        # > none
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn create convex

        # 选择用户认证：
        # > none
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bunx create-convex

        # 选择用户认证：
        # > none
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ### 运行 `convex dev` [#运行-convex-dev]

    在设置过程中运行 CLI 将初始化你的 Convex 部署
    如果它还不存在，并在此过程中保持生成的类型为最新状态。请保持它运行。

    <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 convex dev
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm dlx convex dev
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn dlx convex dev
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun x convex dev
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Step>

  <Step>
    ## 安装包 [#安装包]

    安装 Better Auth 和 Better Auth 的 Convex 组件，并确保你使用的是最新版本的 Convex。

    <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 @convex-dev/better-auth
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add better-auth @convex-dev/better-auth
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add better-auth @convex-dev/better-auth
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add better-auth @convex-dev/better-auth
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Callout type="info">
      [`@convex-dev/better-auth`](https://github.com/get-convex/better-auth) 由 Convex 维护。
    </Callout>
  </Step>

  <Step>
    ## 设置环境变量 [#设置环境变量]

    生成用于加密和生成哈希的密钥。如果你已安装 openssl，请使用下面的命令，或者使用 `npx auth secret` 生成一个。

    <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 convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm dlx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn dlx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun x convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    将你的网站 URL 添加到你的 Convex 部署中。

    <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 convex env set SITE_URL http://localhost:3000
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm dlx convex env set SITE_URL http://localhost:3000
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn dlx convex env set SITE_URL http://localhost:3000
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun x convex env set SITE_URL http://localhost:3000
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    将环境变量添加到 `npx convex dev` 创建的 `.env.local` 文件中。
    你的框架开发服务器会读取它。

    <CodeBlockTabs defaultValue="云端">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="云端">
          云端
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="自托管">
          自托管
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="云端">
        ```sh title=".env.local" 
        # `npx convex dev` 使用的部署
        CONVEX_DEPLOYMENT=dev:adjective-animal-123 # 团队：team-name，项目：project-name

        NEXT_PUBLIC_CONVEX_URL=https://adjective-animal-123.convex.cloud

        # 与 NEXT_PUBLIC_CONVEX_URL 相同，但以 .site 结尾 // [!code ++]
        NEXT_PUBLIC_CONVEX_SITE_URL=https://adjective-animal-123.convex.site # [!code ++]

        # 你本地的网站 URL // [!code ++]
        NEXT_PUBLIC_SITE_URL=http://localhost:3000 # [!code ++]
        ```
      </CodeBlockTab>

      <CodeBlockTab value="自托管">
        ```sh title=".env.local" 
        # `npx convex dev` 使用的部署
        CONVEX_DEPLOYMENT=dev:adjective-animal-123 # 团队：team-name，项目：project-name

        NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210

        # 通常会比 NEXT_PUBLIC_CONVEX_URL 高一个数字，
        # 所以如果你的 convex url 是 :3212，那么你的网站 url 将是 :3213
        NEXT_PUBLIC_CONVEX_SITE_URL=http://127.0.0.1:3211 # [!code ++]

        # 你本地的网站 URL // [!code ++]
        NEXT_PUBLIC_SITE_URL=http://localhost:3000 # [!code ++]
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Callout type="info">
      由于 Better Auth 实例运行在 Convex 上，auth 实例使用的环境变量应通过 Convex CLI 或控制台进行配置，而不是放在 `.env.local` 中。

      例如：`BETTER_AUTH_SECRET`、`GITHUB_CLIENT_ID`、`GITHUB_CLIENT_SECRET` ...
    </Callout>
  </Step>

  <Step>
    ## 添加 Convex 认证配置 [#添加-convex-认证配置]

    添加一个 `convex/auth.config.ts` 文件，以将 Better Auth 配置为认证提供方。

    ```ts title="convex/auth.config.ts"
    import { getAuthConfigProvider } from "@convex-dev/better-auth/auth-config";
    import type { AuthConfig } from "convex/server";

    export default {
      providers: [getAuthConfigProvider()],
    } satisfies AuthConfig;
    ```
  </Step>

  <Step>
    ## 创建 Better Auth Convex 组件 [#创建-better-auth-convex-组件]

    Convex 组件可以从 NPM 或本地文件夹安装。虽然 [NPM 版本](https://labs.convex.dev/better-auth) 可用，但本指南使用本地文件夹设置，以充分发挥 Better Auth 的全部潜力。

    ### 创建组件定义 [#创建组件定义]

    创建一个 `convex/betterAuth/convex.config.ts` 文件来定义该组件。这将向 Convex 表明 `convex/betterAuth` 目录是一个本地安装的组件。

    ```ts title="convex/betterAuth/convex.config.ts"
    import { defineComponent } from "convex/server";

    const component = defineComponent("betterAuth");

    export default component;
    ```

    ### 注册组件 [#注册组件]

    在你的 Convex 项目中注册 Better Auth 组件。

    ```ts title="convex/convex.config.ts"
    import { defineApp } from "convex/server";
    import betterAuth from "./betterAuth/convex.config";

    const app = defineApp();

    app.use(betterAuth);

    export default app;
    ```

    ### 创建 Better Auth 实例 [#创建-better-auth-实例]

    创建一个 Better Auth 实例并初始化该组件。

    <Callout type="info">
      * 在下一步之前，此文件尚未完成。
      * 在你保存该文件之前，会显示一些 TypeScript 错误。
    </Callout>

    ```ts title="convex/betterAuth/auth.ts"
    import { createClient } from "@convex-dev/better-auth";
    import { convex } from "@convex-dev/better-auth/plugins";
    import type { GenericCtx } from "@convex-dev/better-auth/utils";
    import type { BetterAuthOptions } from "better-auth";
    import { betterAuth } from "better-auth";
    import { components } from "../_generated/api";
    import type { DataModel } from "../_generated/dataModel";
    import authConfig from "../auth.config";
    import schema from "./schema";

    // Better Auth 组件
    export const authComponent = createClient<DataModel, typeof schema>(
      components.betterAuth,
      {
        local: { schema },
        verbose: false,
      },
    );

    // Better Auth 选项
    export const createAuthOptions = (ctx: GenericCtx<DataModel>) => {
      return {
        appName: "My App",
        baseURL: process.env.SITE_URL,
        secret: process.env.BETTER_AUTH_SECRET,
        database: authComponent.adapter(ctx),
        emailAndPassword: {
          enabled: true,
        },
        plugins: [convex({ authConfig })],
      } satisfies BetterAuthOptions;
    };

    // 供 `auth` CLI 使用
    export const options = createAuthOptions({} as GenericCtx<DataModel>);

    // Better Auth 实例
    export const createAuth = (ctx: GenericCtx<DataModel>) => {
      return betterAuth(createAuthOptions(ctx));
    };
    ```

    ### 生成 schema [#生成-schema]

    配置好你的 Better Auth 实例后，运行下面的命令来生成 `convex/betterAuth/schema.ts`。

    <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 generate --config ./convex/betterAuth/auth.ts --output ./convex/betterAuth/schema.ts
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm dlx auth generate --config ./convex/betterAuth/auth.ts --output ./convex/betterAuth/schema.ts
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn dlx auth generate --config ./convex/betterAuth/auth.ts --output ./convex/betterAuth/schema.ts
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun x auth generate --config ./convex/betterAuth/auth.ts --output ./convex/betterAuth/schema.ts
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Callout type="info">
      如果你需要修改 Better Auth 实例，请使用此命令更新 schema。
    </Callout>

    ### 导出适配器函数 [#导出适配器函数]

    为 Better Auth 组件导出适配器函数。

    ```ts title="convex/betterAuth/adapter.ts"
    import { createApi } from "@convex-dev/better-auth";
    import { createAuthOptions } from "./auth";
    import schema from "./schema";

    export const {
      create,
      findOne,
      findMany,
      updateOne,
      updateMany,
      deleteOne,
      deleteMany,
    } = createApi(schema, createAuthOptions);
    ```
  </Step>

  <Step>
    ## 创建 Better Auth 客户端实例 [#创建-better-auth-客户端实例]

    创建 Better Auth 客户端实例，用于从你的客户端与 Better Auth 服务器交互。

    ```ts title="lib/auth-client.ts"
    import { convexClient } from "@convex-dev/better-auth/client/plugins";
    import { createAuthClient } from "better-auth/react";

    export const authClient = createAuthClient({
      plugins: [convexClient()],
    });
    ```
  </Step>

  <Step>
    ## 配置 Next.js 服务端辅助函数 [#配置-nextjs-服务端辅助函数]

    配置一组用于已认证 SSR、服务端函数和路由处理程序的辅助函数。

    ```ts title="lib/auth-server.ts"
    import { convexBetterAuthNextJs } from "@convex-dev/better-auth/nextjs";

    export const {
      handler,
      preloadAuthQuery,
      isAuthenticated,
      getToken,
      fetchAuthQuery,
      fetchAuthMutation,
      fetchAuthAction,
    } = convexBetterAuthNextJs({
      convexUrl: process.env.NEXT_PUBLIC_CONVEX_URL!,
      convexSiteUrl: process.env.NEXT_PUBLIC_CONVEX_SITE_URL!,
    });
    ```
  </Step>

  <Step>
    ## 挂载处理程序 [#挂载处理程序]

    在你的 Convex 部署上注册 Better Auth 路由处理程序。

    ```ts title="convex/http.ts"
    import { httpRouter } from "convex/server";
    import { authComponent, createAuth } from "./betterAuth/auth";

    const http = httpRouter();

    authComponent.registerRoutes(http, createAuth);

    export default http;
    ```

    设置路由处理程序，将来自 Next.js 的认证请求代理到你的 Convex 部署。

    ```ts title="app/api/auth/[...all]/route.ts"
    import { handler } from "@/lib/auth-server";

    export const { GET, POST } = handler;
    ```
  </Step>

  <Step>
    ## 设置 Convex 客户端提供方 [#设置-convex-客户端提供方]

    创建一个 `ConvexClientProvider` 组件。

    ```tsx title="components/ConvexClientProvider.tsx"
    "use client";

    import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
    import { ConvexReactClient } from "convex/react";
    import { authClient } from "@/lib/auth-client";

    const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

    export function ConvexClientProvider({
      children,
      initialToken,
    }: {
      children: React.ReactNode;
      initialToken?: string | null;
    }) {
      return (
        <ConvexBetterAuthProvider
          client={convex}
          authClient={authClient}
          initialToken={initialToken}
        >
          {children}
        </ConvexBetterAuthProvider>
      );
    }
    ```

    使用 `ConvexClientProvider` 组件包装你的应用。

    ```tsx title="app/layout.tsx"
    import { ConvexClientProvider } from "@/components/ConvexClientProvider";
    import { getToken } from "@/lib/auth-server";

    export default async function RootLayout({
      children,
    }: Readonly<{
      children: React.ReactNode;
    }>) {
      const token = await getToken();
      return (
        <html>
          <body>
            <ConvexClientProvider initialToken={token}>
              {children}
            </ConvexClientProvider>
          </body>
        </html>
      );
    }
    ```
  </Step>
</Steps>

## 完成 🎉 [#完成-]

您现已准备好开始使用 Better Auth 结合 Convex。

***

## 使用方法 [#使用方法]

### 创建函数 [#创建函数]

Better Auth 的 `auth.api` 方法通常运行在服务器端，但当 Convex 作为您的后端时，这些方法需要在 Convex 函数中运行。然后，客户端可通过像 `useMutation` 这样的钩子调用这些函数，也可以在服务器函数或其他服务器代码中使用诸如 `fetchAuthMutation` 之类的 auth-server 工具调用。

Convex 习惯将函数放在 `convex/` 目录。例如，您可以在 `convex/auth.ts` 文件中创建与认证相关的函数，如下：

```ts title="convex/auth.ts"
import { query } from "./_generated/server";

export const getCurrentUser = query({
  args: {},
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();
    return identity;
  },
});

// ... 其他函数
```

### 使用 Better Auth 客户端 [#使用-better-auth-客户端]

您可以像通常使用 Better Auth 客户端一样使用 `authClient`。

```tsx title="app/sign-in/page.tsx"
"use client";

import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth-client";

const Page = () => {
  return (
    <div className="flex flex-col items-center justify-center h-screen">
      <Button
        onClick={async () => {
          await authClient.signIn.social({
            provider: "github",
            callbackURL: "/dashboard",
          });
        }}
      >
        通过 GitHub 登录
      </Button>
    </div>
  );
};

export default Page;
```

### 使用 Convex React 钩子 [#使用-convex-react-钩子]

如果您创建了 Convex 函数，可以在客户端使用 Convex React 钩子查询或变更数据，就像平常一样。

```tsx title="app/dashboard/page.tsx"
"use client";

import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";

const Page = () => {
  const user = useQuery(api.auth.getCurrentUser);

  if (user === undefined) {
    return <div>加载中...</div>;
  }

  if (user === null) {
    return <div>未授权</div>;
  }

  return (
    <div>
      <pre>{JSON.stringify(user, null, 2)}</pre>
    </div>
  );
};

export default Page;
```

### 使用服务器辅助函数 [#使用服务器辅助函数]

您可以在服务器端使用辅助函数进行身份认证相关操作。

```tsx title="app/protected/page.tsx"
import { isAuthenticated } from "@/lib/auth-server";

const Page = async () => {
  const hasToken = await isAuthenticated();
  if (!hasToken) {
    return <div>未授权</div>;
  }

  return (
    <div>
      <p>您好</p>
    </div>
  );
};

export default Page;
```

### 使用 SSR 服务器组件 [#使用-ssr-服务器组件]

Convex 查询可以在服务器组件中预加载，并通过 `preloadAuthQuery` 和 `usePreloadedAuthQuery` 在客户端组件中渲染。

<Callout type="info">
  `preloadAuthQuery` 建议在您希望根据数据状态处理不同 UI 时使用，而不是用于保护资源。
</Callout>

*在服务器组件中预加载：*

```tsx title="app/landing/page.tsx"
import { api } from "@/convex/_generated/api";
import { preloadAuthQuery } from "@/lib/auth-server";
import Header from "./header";

const Page = async () => {
  const preloadedUserQuery = await preloadAuthQuery(api.auth.getCurrentUser);

  return (
    <div>
      <Header preloadedUserQuery={preloadedUserQuery} />
    </div>
  );
};

export default Page;
```

*在客户端组件中渲染预加载的数据：*

```tsx title="app/landing/header.tsx"
"use client";

import { usePreloadedAuthQuery } from "@convex-dev/better-auth/nextjs/client";
import type { Preloaded } from "convex/react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import type { api } from "@/convex/_generated/api";

export const Header = ({
  preloadedUserQuery,
}: {
  preloadedUserQuery: Preloaded<typeof api.auth.getCurrentUser>;
}) => {
  const user = usePreloadedAuthQuery(preloadedUserQuery);
  return (
    <div>
      {user ? (
        <Button asChild>
          <Link href="/dashboard">仪表盘</Link>
        </Button>
      ) : (
        <Button asChild>
          <Link href="/sign-in">登录</Link>
        </Button>
      )}
    </div>
  );
};

export default Header;
```

## 额外资源 [#额外资源]

* [Convex 文档](https://docs.convex.dev/home)
* [`@convex-dev/better-auth` 文档](https://labs.convex.dev/better-auth)
* [`@convex-dev/better-auth` GitHub 仓库](https://github.com/get-convex/better-auth)

