关于 trigger-realtime
面向 Trigger.dev 前端开发的实时数据与流处理指南,适用于构建 React、Next.js 或 Remix 界面。介绍运行状态订阅、浏览器触发任务及读写流等模式。
主要功能
- 01订阅实时运行状态
- 02渲染 AI 与文本流
- 03从浏览器触发任务
- 04向运行中的任务发送输入
- 05通过 React 完成等待令牌
适用场景
- 01在前端显示任务进度
- 02构建 AI 输出流界面
- 03从浏览器提交任务并跟踪运行
- 04通过界面处理任务审批
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ triggerdotdev/ skills --skill trigger-realtime安装后在 Claude Code 中输入/trigger-realtime确认已加载。
详细介绍
实时功能与前端
Trigger.dev 运行状态和流的消费端:读取运行的实时更新、渲染 AI/文本流,并从浏览器触发任务。钩子来自 @trigger.dev/react-hooks;令牌生成和后端订阅来自 @trigger.dev/sdk。
设置
npm add @trigger.dev/react-hooks # frontend hooks (React/Next.js/Remix)
# @trigger.dev/sdk is already installed for the backend
流程始终是:在后端生成作用域受限的令牌,将其传递给前端,再使用钩子订阅。
// backend (API route / server action)
import { auth } from "@trigger.dev/sdk";
const publicAccessToken = await auth.createPublicToken({
scopes: { read: { runs: ["run_1234"] } }, // a token with no scopes is useless
});
// frontend
"use client";
import { useRealtimeRun } from "@trigger.dev/react-hooks";
export function RunStatus({ runId, publicAccessToken }: { runId: string; publicAccessToken: string }) {
const { run, error } = useRealtimeRun(runId, { accessToken: publicAccessToken });
if (error) return <div>Error: {error.message}</div>;
if (!run) return <div>Loading...</div>;
return <div>Run: {run.status}</div>;
}
令牌分为两种:Public Access Tokens(读取/订阅,通过 auth.createPublicToken 生成)和 Trigger Tokens(从浏览器触发,单次使用,通过 auth.createTriggerPublicToken 生成)。两者的默认有效期均为 15 分钟。
核心模式
1. 订阅运行并渲染元数据进度
metadata 的类型为 Record<string, DeserializedJson>,因此嵌套值需要进行类型转换。
"use client";
import { useRealtimeRun } from "@trigger.dev/react-hooks";
import type { myTask } from "@/trigger/myTask";
export function Progress({ runId, publicAccessToken }: { runId: string; publicAccessToken: string }) {
const { run, error } = useRealtimeRun<typeof myTask>(runId, { accessToken: publicAccessToken });
if (error) return <div>Error: {error.message}</div>;
if (!run) return <div>Loading...</div>;
const progress = run.metadata?.progress as { percentage?: number } | undefined;
return <div>{run.status}: {progress?.percentage ?? 0}%</div>;
}
传入 onComplete: (run, error) => {},即可在运行结束时作出响应。
2. 使用 skipColumns 仅订阅状态
对于徽标或进度条,无需使用 payload/output。跳过这些字段可减小传输数据量,并避免出现“Large HTTP Payload”警告。
const { run } = useRealtimeRun(runId, {
accessToken: publicAccessToken,
skipColumns: ["payload", "output"],
});
你可以跳过以下任意字段:payload、output、metadata、startedAt、delayUntil、queuedAt、expiredAt、completedAt、number、isTest、usageDurationMs、costInCents、baseCostInCents、ttl、payloadType、outputType、runTags、error。
3. 使用 Trigger Token 从浏览器触发任务
此处的 accessToken 是 Trigger Token(通过 auth.createTriggerPublicToken 生成),而不是 Public Access Token。
"use client";
import { useTaskTrigger } from "@trigger.dev/react-hooks";
import type { myTask } from "@/trigger/myTask";
export function TriggerButton({ triggerToken }: { triggerToken: string }) {
const { submit, handle, isLoading } = useTaskTrigger<typeof myTask>("my-task", {
accessToken: triggerToken,
});
if (handle) return <div>Run ID: {handle.id}</div>;
return (
<button onClick={() => submit({ foo: "bar" }, { tags: ["user:123"] })} disabled={isLoading}>
{isLoading ? "Triggering..." : "Run"}
</button>
);
}
submit(payload, options?) 接受与后端 trigger 调用相同的选项。
4. 在一个钩子中触发任务并订阅
"use client";
import { useRealtimeTaskTrigger } from "@trigger.dev/react-hooks";
import type { myTask } from "@/trigger/myTask";
export function Runner({ publicAccessToken }: { publicAccessToken: string }) {
const { submit, run, isLoading } = useRealtimeTaskTrigger<typeof myTask>("my-task", {
accessToken: publicAccessToken,
});
if (run) return <div>{run.status}</div>;
return <button onClick={() => submit({ foo: "bar" })} disabled={isLoading}>Run</button>;
}
如果还需要任务的流,请使用 useRealtimeTaskTriggerWithStreams<typeof myTask, STREAMS>(它会返回 { submit, run, streams, error, isLoading })。
5. 消费 AI/文本流(SDK 4.1.0+,推荐)
useRealtimeStream 接受一个已定义的流以获得完整的类型安全性,也可以接受 runId 和可选的流键。返回 { parts, error }。
"use client";
import { useRealtimeStream } from "@trigger.dev/react-hooks";
import { aiStream } from "@/trigger/streams"; // a defined stream -> typed parts
export function StreamView({ runId, publicAccessToken }: { runId: string; publicAccessToken: string }) {
const { parts, error } = useRealtimeStream(aiStream, runId, {
accessToken: publicAccessToken,
timeoutInSeconds: 300, // default 60
onData: (chunk) => console.log(chunk),
});
if (error) return <div>Error: {error.message}</div>;
if (!parts) return <div>Loading...</div>;
return <div>{parts.join("")}</div>;
}
不使用已定义的流时:useRealtimeStream<string>(runId, "ai-output", { accessToken }),也可以省略键以使用默认流。其他选项:baseURL、startIndex、throttleInMs(默认值为 16)。如果需要同时获取运行和所有流,仍可使用旧版 useRealtimeRunWithStreams(runId, options) 钩子。
6. 向正在运行的任务发送输入
"use client";
import { useInputStreamSend } from "@trigger.dev/react-hooks";
import { approval } from "@/trigger/streams";
export function ApprovalForm({ runId, accessToken }: { runId: string; accessToken: string }) {
const { send, isLoading, isReady } = useInputStreamSend(approval.id, runId, { accessToken });
return (
<button disabled={!isReady || isLoading} onClick={() => send({ approved: true })}>
Approve
</button>
);
}
7. 从 React 完成等待令牌
// backend: create the token, return id + publicAccessToken to the frontend
import { wait } from "@trigger.dev/sdk";
const token = await wait.createToken({ timeout: "10m" });
return { tokenId: token.id, publicToken: token.publicAccessToken };
"use client";
import { useWaitToken } from "@trigger.dev/react-hooks";
export function Approve({ tokenId, publicToken }: { tokenId: string; publicToken: string }) {
const { complete } = useWaitToken(tokenId, { accessToken: publicToken });
return <button onClick={() => complete({ approved: true })}>Approve</button>;
}
8. 从后端订阅(异步迭代器)
import { runs, tasks } from "@trigger.dev/sdk";
import type { myTask } from "./trigger/my-task";
const handle = await tasks.trigger("my-task", { some: "data" });
for await (const run of runs.subscribeToRun<typeof myTask>(handle.id)) {
console.log(run.payload.some, run.output?.some); // typed
}
runs.subscribeToRun 会在运行结束时完成,因此循环会自动退出。
常见错误
-
严重:使用公共访问令牌从浏览器触发任务。
createPublicToken返回的读取令牌无法触发任务。- 错误:
useTaskTrigger("my-task", { accessToken: publicAccessTokenFromCreatePublicToken }) - 正确:使用
auth.createTriggerPublicToken("my-task")创建一次性触发令牌,并将其传入。
- 错误:
-
令牌没有作用域。 不带作用域的令牌无法授权任何操作,因此每次订阅都会返回 403。
- 错误:
await auth.createPublicToken() - 正确:
await auth.createPublicToken({ scopes: { read: { runs: ["run_1234"] } } })
- 错误:
-
使用
useRun/SWR 轮询实时更新。useRun是基于 SWR 的管理 API Hook(不建议用于实时状态);如果确实要用,请将refreshInterval: 0设为停止轮询。- 错误:使用
useRun(runId, { refreshInterval: 1000 })跟踪进度 - 正确:
useRealtimeRun(runId, { accessToken })(无需轮询,也无需设置 WebSocket)
- 错误:使用
-
忘记添加
"use client"。 实时/触发 Hook 无法在服务端组件中运行。- 错误:在 Next.js App Router 服务端组件中使用
useRealtimeRun - 正确:在使用这些 Hook 的任意组件顶部添加
"use client";。
- 错误:在 Next.js App Router 服务端组件中使用
-
传输不会呈现的
payload/output。- 错误:为了显示状态徽标而使用
useRealtimeRun(runId, { accessToken })(会通过网络传输大型负载) - 正确:
useRealtimeRun(runId, { accessToken, skipColumns: ["payload", "output"] })
- 错误:为了显示状态徽标而使用
-
在句柄尚不存在时订阅。
- 错误:不添加条件保护,直接使用
useRealtimeRun(handle, { accessToken: handle?.publicAccessToken }) - 正确:添加
enabled: !!handle,确保仅在触发器返回句柄后才订阅。
- 错误:不添加条件保护,直接使用
参考资料
相关技能:
trigger-tasks,用于任务端:streams.define()、metadata.set()和wait.createToken。trigger-authoring-chat-agent和trigger-chat-agent-advanced,用于聊天 Agent,它们基于这些实时流构建。
参考文档与此技能一同包含在同一软件包中,请在本地读取(无需联网),并使用与你安装版本匹配的文档。上方的 sources: frontmatter 列出了此技能所依据的所有文档,均位于 @trigger.dev/sdk/docs/ 下。请从以下文档开始:
@trigger.dev/sdk/docs/realtime/react-hooks/subscribe.mdx@trigger.dev/sdk/docs/realtime/react-hooks/streams.mdx@trigger.dev/sdk/docs/realtime/auth.mdx@trigger.dev/sdk/docs/realtime/run-object.mdx(实时运行对象与useRun返回的管理 API 对象不同)
版本
此技能包含在 @trigger.dev/sdk 中,并直接从 node_modules 读取,因此始终与你安装的 SDK 版本匹配(请参见旁边的 package.json)。这些 API 的完整文档也随其一同包含在 @trigger.dev/sdk/docs/ 下。