TanStack Start 集成
将 Better Auth 集成到 TanStack Start。
本集成指南假设您正在使用 TanStack Start。
开始之前,请确保您已配置好 Better Auth 实例。如果还没有配置,请查看 安装指南。
快速开始
您可以使用以下命令创建一个集成了 Better Auth 的全新 TanStack Start 项目。该 CLI 会设置一个配置了插件和挂载处理程序的身份认证实例的项目。
npm create @tanstack/start
◇ 你希望为项目添加哪些附加组件?
│ Better Auth用法
挂载处理程序
我们需要将处理程序挂载到 TanStack API 端点/服务器路由。
创建新文件:/src/routes/api/auth/$.ts
import { auth } from '@/lib/auth'
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/api/auth/$')({
server: {
handlers: {
GET: async ({ request }:{ request: Request }) => {
return await auth.handler(request)
},
POST: async ({ request }:{ request: Request }) => {
return await auth.handler(request)
},
},
},
})使用技巧
- 我们建议使用客户端 SDK 或
authClient来处理身份验证,而不是使用带有auth.api的服务器操作。 - 当您调用需要设置 Cookie 的函数(如
signInEmail或signUpEmail)时,您需要为 TanStack Start 处理 Cookie 设置。Better Auth 提供了tanstackStartCookies插件来自动为您处理此问题。
React(TanStack Start + React)示例:
import { betterAuth } from "better-auth";
import { tanstackStartCookies } from "better-auth/tanstack-start";
export const auth = betterAuth({
//...你的配置
plugins: [tanstackStartCookies()] // 确保这是数组中最后一个插件
})Solid.js(TanStack Start + Solid)示例:
import { betterAuth } from "better-auth";
import { tanstackStartCookies } from "better-auth/tanstack-start/solid";
export const auth = betterAuth({
//...你的配置
plugins: [tanstackStartCookies()] // 确保这是数组中最后一个插件
})这样,当您调用设置 Cookie 的函数时,Cookie 会自动通过 TanStack Start 的 Cookie 处理系统进行设置。
import { auth } from "@/lib/auth"
const signIn = async () => {
await auth.api.signInEmail({
body: {
email: "[email protected]",
password: "password",
}
})
}保护资源
要保护需要认证才能访问的资源,请使用带有服务器函数的 beforeLoad。这确保每次导航时都会进行身份认证检查,包括通过 <Link> 进行的客户端导航。
首先,创建服务器端的会话检查辅助函数:
import { createServerFn } from "@tanstack/react-start";
import { getRequestHeaders } from "@tanstack/react-start/server";
import { auth } from "@/lib/auth";
export const getSession = createServerFn({ method: "GET" }).handler(async () => {
const headers = getRequestHeaders();
const session = await auth.api.getSession({ headers });
return session;
});
export const ensureSession = createServerFn({ method: "GET" }).handler(async () => {
const headers = getRequestHeaders();
const session = await auth.api.getSession({ headers });
if (!session) {
throw new Error("Unauthorized");
}
return session;
});保护单个路由
在路由定义中使用 beforeLoad:
import { createFileRoute, redirect } from '@tanstack/react-router'
import { getSession } from '@/lib/auth.functions'
export const Route = createFileRoute('/dashboard')({
beforeLoad: async () => {
const session = await getSession();
if (!session) {
throw redirect({ to: "/login" });
}
return { user: session.user };
},
component: Dashboard,
})
function Dashboard() {
const { user } = Route.useRouteContext();
return <div>欢迎,{user.name}!</div>
}保护多个路由(布局)
要保护多个路由,请使用无路径的布局路由:
import { createFileRoute, redirect, Outlet } from '@tanstack/react-router'
import { getSession } from '@/lib/auth.functions'
export const Route = createFileRoute('/_protected')({
beforeLoad: async ({ location }) => {
const session = await getSession();
if (!session) {
throw redirect({
to: "/login",
search: { redirect: location.href },
});
}
return { user: session.user };
},
component: () => <Outlet />,
})然后将受保护的路由嵌套到 _protected 下:
dashboard.tsx
settings.tsx
_protected.tsx
login.tsx
保护服务器函数
使用 ensureSession 辅助函数保护服务器函数:
import { createServerFn } from "@tanstack/react-start";
import { ensureSession } from "./auth.functions";
export const createPost = createServerFn({ method: "POST" })
.inputValidator((data: { title: string }) => data)
.handler(async ({ data }) => {
const session = await ensureSession();
const post = await db.posts.create({
title: data.title,
authorId: session.user.id,
});
return post;
});