凸积分
本节内容改编自 Convex + Better Auth 指南,如需更多信息,请参考他们的文档。
先决条件
🌐 Prerequisites
创建一个 Convex 项目
🌐 Create a Convex project
要使用 Convex + Better Auth,你首先需要一个 Convex 项目。如果你还没有,请运行以下命令开始。
🌐 To use Convex + Better Auth, you'll first need a Convex project. If you don't have one, run the following command to get started.
npm create convex@latest查看 Convex 文档 以了解更多关于 Convex 的信息。
🌐 Check out the Convex docs to learn more about Convex.
运行 convex dev
🌐 Run convex dev
在设置过程中运行 CLI 将初始化你的 Convex 部署(如果它尚不存在),并在整个过程中保持生成的类型为最新状态。请保持其运行。
🌐 Running the CLI during setup will initialize your Convex deployment if it doesn't already exist, and keeps generated types current through the process. Keep it running.
npx convex dev安装 Convex + Better Auth
🌐 Installation of Convex + Better Auth
以下文档假设你正在使用 Next.js。
🌐 The following documentation assumes you're using Next.js.
如果你没有使用 Next.js,其他框架的支持可以在 Convex 安装指南 中找到。
🌐 If you're not using Next.js, support for other frameworks is documented in the installation guide by Convex.
有关完整示例,请查看使用 Next.js 的 Convex + Better Auth 示例 在 GitHub 上。
安装
🌐 Installation
安装软件包
安装组件和 Better Auth。确保使用 Convex 的 1.25.0 或更高版本。
npm install better-auth
npm install convex@latest @convex-dev/better-authRegister the component
在你的 Convex 项目中注册 Better Auth 组件。
import { defineApp } from "convex/server";
import betterAuth from "@convex-dev/better-auth/convex.config";
const app = defineApp();
app.use(betterAuth);
export default app;Add Convex auth config
添加一个 convex/auth.config.ts 文件来将 Better Auth 配置为身份验证提供程序。
import { getAuthConfigProvider } from "@convex-dev/better-auth/auth-config";
import type { AuthConfig } from "convex/server";
export default {
providers: [getAuthConfigProvider()],
} satisfies AuthConfig;Set environment variables
生成用于加密和生成哈希的密钥。如果你已经安装了 openssl,可以使用下面的命令,或者用你喜欢的方式生成自己的密钥。
npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)将你的网站 URL 添加到你的 Convex 部署中。
npx convex env set SITE_URL http://localhost:3000将环境变量添加到由 npx convex dev 创建的 .env.local 文件中。你的框架开发服务器会自动读取它们。
# Deployment used by \`npx convex dev\`
CONVEX_DEPLOYMENT=dev:adjective-animal-123 # team: team-name, project: project-name
NEXT_PUBLIC_CONVEX_URL=https://adjective-animal-123.convex.cloud
# Same as NEXT_PUBLIC_CONVEX_URL but ends in .site
NEXT_PUBLIC_CONVEX_SITE_URL=https://adjective-animal-123.convex.site
# Your local site URL
NEXT_PUBLIC_SITE_URL=http://localhost:3000Create a Better Auth instance
Create a Better Auth instance and initialize the component.
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
import { convex } from "@convex-dev/better-auth/plugins";
import { components } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
import { query } from "./_generated/server";
import { betterAuth } from "better-auth";
import authConfig from "./auth.config";
const siteUrl = process.env.SITE_URL!;
// The component client has methods needed for integrating Convex with Better Auth,
// as well as helper methods for general use.
export const authComponent = createClient<DataModel>(components.betterAuth);
export const createAuth = (ctx: GenericCtx<DataModel>) => {
return betterAuth({
baseURL: siteUrl,
database: authComponent.adapter(ctx),
// Configure simple, non-verified email/password to get started
emailAndPassword: {
enabled: true,
requireEmailVerification: false,
},
plugins: [
// The Convex plugin is required for Convex compatibility
convex({ authConfig }),
],
});
};
// Example function for getting the current user
// Feel free to edit, omit, etc.
export const getCurrentUser = query({
args: {},
handler: async (ctx) => {
return authComponent.getAuthUser(ctx);
},
});Create a Better Auth client instance
为你的客户端创建一个更好的 Auth 客户端实例,以便与 Better Auth 服务器交互。
import { createAuthClient } from "better-auth/react";
import { convexClient } from "@convex-dev/better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [convexClient()],
});Configure Next.js server utilities
为经过身份验证的 SSR、服务器功能和路由处理程序配置一组辅助函数。
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!,
});Mount handlers
在你的 Convex 部署上注册更好的认证路由处理程序。
import { httpRouter } from "convex/server";
import { authComponent, createAuth } from "./auth";
const http = httpRouter();
authComponent.registerRoutes(http, createAuth);
export default http;设置路由处理程序,将来自框架服务器的身份验证请求代理到你的 Convex 部署。
import { handler } from "@/lib/auth-server";
export const { GET, POST } = handler;Set up Convex client provider
用 ConvexBetterAuthProvider 组件封装你的应用。
"use client";
import { type PropsWithChildren } from "react";
import { ConvexReactClient } from "convex/react";
import { authClient } from "@/lib/auth-client";
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
export function ConvexClientProvider({
children,
initialToken,
}: PropsWithChildren<{ initialToken?: string | null }>) {
return (
<ConvexBetterAuthProvider
client={convex}
authClient={authClient}
initialToken={initialToken}
>
{children}
</ConvexBetterAuthProvider>
);
}你完成了!
🌐 You're done!
你现在可以开始在 Convex 上使用 Better Auth 了。
🌐 You're now ready to start using Better Auth with Convex.
用法
🌐 Usage
查看 基本用法 指南,了解更多关于通用用法的信息。以下是针对 Next.js 的使用注意事项。
🌐 Check out the Basic Usage guide for more information on general usage. Below are usage notes specific to Next.js.
使用服务器组件的 SSR
🌐 SSR with server components
凸查询可以在服务器组件中预加载,并通过 preloadAuthQuery 和 usePreloadedAuthQuery 在客户端组件中渲染。
🌐 Convex queries can be preloaded in server components and rendered in client
components via preloadAuthQuery and usePreloadedAuthQuery.
在服务器组件中预加载:
🌐 Preloading in a server component:
import { preloadAuthQuery } from "@/lib/auth-server";
import { api } from "@/convex/_generated/api";
const Page = async () => {
const [preloadedUserQuery] = await Promise.all([
preloadAuthQuery(api.auth.getCurrentUser),
// Load multiple queries in parallel if needed
]);
return (
<div>
<Header preloadedUserQuery={preloadedUserQuery} />
</div>
);
};
export default Page;Rendering preloaded data in a client component:
import { usePreloadedAuthQuery } from "@convex-dev/better-auth/nextjs/client";
import { api } from "@/convex/_generated/api";
export const Header = ({
preloadedUserQuery,
}: {
preloadedUserQuery: Preloaded<typeof api.auth.getCurrentUser>;
}) => {
const user = usePreloadedAuthQuery(preloadedUserQuery);
return (
<div>
<h1>{user?.name}</h1>
</div>
);
};
export default Header;在服务器代码中使用 Better Auth
🌐 Using Better Auth in server code
Better Auth 的 auth.api 方法 通常会在你的 Next.js 服务器代码中运行,但如果 Convex 是你的后端,这些方法需要在 Convex 函数中运行。然后可以通过像 useMutation 这样的钩子从客户端调用该 Convex 函数,或者在服务器函数和其他服务器代码中使用其中一个 auth-server 工具(如 fetchAuthMutation)调用。身份验证会自动通过会话 cookie 处理。
🌐 Better Auth's
auth.api methods would
normally run in your Next.js server code, but with Convex being your backend,
these methods need to run in a Convex function. The Convex function can then be
called from the client via hooks like useMutation or in server functions and
other server code using one of the auth-server utilities like
fetchAuthMutation. Authentication is handled automatically using session
cookies.
这是一个使用 changePassword 方法的示例。Better Auth 的 auth.api 方法在 Convex 变更中被调用,因为我们知道该函数需要写入权限。对于读取操作,可以使用查询函数。
🌐 Here's an example using the changePassword method. The Better Auth auth.api
method is called inside of a Convex mutation, because we know this function
needs write access. For reads a query function can be used.
import { mutation } from "./_generated/server";
import { v } from "convex/values";
import { createAuth, authComponent } from "./auth";
export const updateUserPassword = mutation({
args: {
currentPassword: v.string(),
newPassword: v.string(),
},
handler: async (ctx, args) => {
const { auth, headers } = await authComponent.getAuth(createAuth, ctx);
await auth.api.changePassword({
body: {
currentPassword: args.currentPassword,
newPassword: args.newPassword,
},
headers,
});
},
});在这里,我们从服务器操作调用变更。
🌐 Here we call the mutation from a server action.
"use server";
import { fetchAuthMutation } from "@/lib/auth-server";
import { api } from "../convex/_generated/api";
// Authenticated mutation via server function
export async function updatePassword({
currentPassword,
newPassword,
}: {
currentPassword: string;
newPassword: string;
}) {
await fetchAuthMutation(api.users.updatePassword, {
currentPassword,
newPassword,
});
}