# TanStack Start 集成 (/docs/integrations/tanstack)

将 Better Auth 集成到 TanStack Start。



本集成指南假设您正在使用 TanStack Start。

开始之前，请确保您已配置好 Better Auth 实例。如果还没有配置，请查看 [安装指南](/docs/installation)。

## 快速开始 [#快速开始]

您可以使用以下命令创建一个集成了 Better Auth 的全新 TanStack Start 项目。该 CLI 会设置一个配置了插件和挂载处理程序的身份认证实例的项目。

<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 @tanstack/start

    ◇  你希望为项目添加哪些附加组件？
    │  Better Auth
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create @tanstack/start

    ◇  你希望为项目添加哪些附加组件？
    │  Better Auth
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create @tanstack/start

    ◇  你希望为项目添加哪些附加组件？
    │  Better Auth
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx @tanstack/create-start

    ◇  你希望为项目添加哪些附加组件？
    │  Better Auth
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## 用法 [#用法]

### 挂载处理程序 [#挂载处理程序]

我们需要将处理程序挂载到 TanStack API 端点/服务器路由。\
创建新文件：`/src/routes/api/auth/$.ts`

```ts title="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）示例：

```ts title="src/lib/auth.ts"
import { betterAuth } from "better-auth";
import { tanstackStartCookies } from "better-auth/tanstack-start";

export const auth = betterAuth({
    //...你的配置
    plugins: [tanstackStartCookies()] // 确保这是数组中最后一个插件
})
```

Solid.js（TanStack Start + Solid）示例：

```ts title="src/lib/auth.ts"
import { betterAuth } from "better-auth";
import { tanstackStartCookies } from "better-auth/tanstack-start/solid";

export const auth = betterAuth({
    //...你的配置
    plugins: [tanstackStartCookies()] // 确保这是数组中最后一个插件
})
```

这样，当您调用设置 Cookie 的函数时，Cookie 会自动通过 TanStack Start 的 Cookie 处理系统进行设置。

```ts
import { auth } from "@/lib/auth"

const signIn = async () => {
    await auth.api.signInEmail({
        body: {
            email: "user@email.com",
            password: "password",
        }
    })
}
```

### 保护资源 [#保护资源]

要保护需要认证才能访问的资源，请使用带有服务器函数的 `beforeLoad`。这确保每次导航时都会进行身份认证检查，包括通过 `<Link>` 进行的客户端导航。

首先，创建服务器端的会话检查辅助函数：

```ts title="src/lib/auth.functions.ts"
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`：

```tsx title="src/routes/dashboard.tsx"
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>
}
```

#### 保护多个路由（布局） [#保护多个路由布局]

要保护多个路由，请使用无路径的布局路由：

```tsx title="src/routes/_protected.tsx"
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` 下：

<Files>
  <Folder name="src">
    <Folder name="routes">
      <Folder name="_protected">
        <File name="dashboard.tsx" />

        <File name="settings.tsx" />
      </Folder>

      <File name="_protected.tsx" />

      <File name="login.tsx" />
    </Folder>
  </Folder>
</Files>

#### 保护服务器函数 [#保护服务器函数]

使用 `ensureSession` 辅助函数保护服务器函数：

```ts title="src/lib/posts.functions.ts"
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;
  });
```

