---
id: mutations
track: learn
product: query
locale: zh-CN
order: 6
revision: 2
sourceCheckedOn: "2026-08-03"
versionRange: "Start ^1 / Query ^5"
verifiedAgainst: "@tanstack/react-start@1.168.34 + @tanstack/react-query@5.101.4"
contentModel: 2
prerequisites:
  - "完成 Loader + Query 课程"
  - "理解服务端输入不能被信任"
sourceRefs:
  - "https://tanstack.com/start/latest/docs/framework/react/guide/server-functions"
  - "https://tanstack.com/query/latest/docs/framework/react/guides/mutations"
  - "https://tanstack.com/query/latest/docs/framework/react/guides/updates-from-mutation-responses"
---

# 从 Server Function 写回缓存

> 验证网络边界上的输入，通过 Mutation 表达写入状态，再用服务端响应不可变地更新 Query 缓存。

**Outcome:** 能实现有 pending、error、success 状态的创建流程。

## 先守住服务端边界

客户端类型不能替代运行时校验。POST Server Function 在 handler 前使用 validator，把 unknown 转换成可信输入；真实应用还应在这里执行认证与授权。

File: `src/features/issues/issues.ts`

```ts
export const createIssue = createServerFn({ method: 'POST' })
  .validator((input: { title: string }) => {
    const title = input.title.trim()
    if (title.length < 3) throw new Error('Title is too short')
    return { title }
  })
  .handler(async ({ data }) => saveIssue(data))
```

## 用响应更新唯一事实来源

useMutation 管理写入生命周期。成功后优先使用服务端返回的完整对象更新缓存；更新函数要返回新数组，不能原地 push 旧缓存。

> **Note:** 如果响应不足以重建正确列表，改用 invalidateQueries 让服务端重新成为事实来源。

File: `src/routes/issues/index.tsx`

```tsx
const queryClient = useQueryClient()
const mutation = useMutation({
  mutationFn: (input: CreateIssueInput) =>
    createIssue({ data: input }),
  onSuccess: (issue) => {
    queryClient.setQueryData<Issue[]>(['issues'], (current = []) => [
      issue,
      ...current,
    ])
  },
})
```

## 实践与验证

### 完成检查点

写入成功后列表和详情缓存保持一致，失败时不会展示未确认的数据。

### 动手练习

创建 Issue 后把响应写入详情缓存，并仅失效受影响的列表前缀。

### 验证方法

模拟成功和失败两条路径；成功后直接进入详情无需额外空状态，失败后缓存保持原值。

### 常见错误

- 无条件失效整个 Query 缓存。
- 假设服务端响应与提交输入完全相同。

## Official sources

- https://tanstack.com/start/latest/docs/framework/react/guide/server-functions
- https://tanstack.com/query/latest/docs/framework/react/guides/mutations
- https://tanstack.com/query/latest/docs/framework/react/guides/updates-from-mutation-responses
