trigger-realtime について
Trigger.dev のフロントエンドでリアルタイム更新やストリームを扱うためのガイドです。React、Next.js、Remix の開発者向けに、実行状態の購読、ブラウザーからのタスク起動、ストリーム入出力を説明します。
主な機能
- 01実行状態のリアルタイム購読
- 02AI・テキストストリームの表示
- 03ブラウザーからのタスク起動
- 04実行中タスクへの入力送信
- 05React での待機トークン完了
活用シーン
- 01フロントエンドでのタスク進捗表示
- 02AI 出力ストリームの画面構築
- 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>;
}
トークンには 2 種類あります。Public Access Tokens(読み取り/サブスクライブ用。auth.createPublicToken で発行)と Trigger Tokens(ブラウザーからのトリガー用。1 回限りで、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 は Public Access Token ではなく、Trigger Token(auth.createTriggerPublicToken)です。
"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. 1 つのフックでトリガーとサブスクライブを行う
"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")で単回使用のトリガートークンを発行し、それを渡します。
- 誤り:
-
スコープのないトークン。 スコープのないトークンでは何も認可されないため、すべての subscribe が 403 になります。
- 誤り:
await auth.createPublicToken() - 正しい方法:
await auth.createPublicToken({ scopes: { read: { runs: ["run_1234"] } } })
- 誤り:
-
ライブ更新に
useRun/SWR でポーリングする。useRunは SWR ベースの管理 API フック(ライブ状態には推奨されません)です。使用する場合は、ポーリングを停止するためにrefreshInterval: 0を設定します。- 誤り:進行状況の追跡に
useRun(runId, { refreshInterval: 1000 })を使う - 正しい方法:
useRealtimeRun(runId, { accessToken })を使う(ポーリングも WebSocket のセットアップも不要です)
- 誤り:進行状況の追跡に
-
"use client"を忘れる。 リアルタイム/トリガーフックはサーバーコンポーネントでは実行できません。- 誤り:
useRealtimeRunを使う Next.js App Router のサーバーコンポーネント - 正しい方法:これらのフックを使うコンポーネントの先頭に
"use client";を記述します。
- 誤り:
-
描画しない
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を参照してください。これらはこのリアルタイムストリームを基盤としています。
参考ドキュメントは同じパッケージ内のこのスキルの隣に含まれています。インストール済みバージョンに固定されたドキュメントを、ネットワークを使わずローカルで読んでください。上記の 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(リアルタイムの run オブジェクトは、useRunが返す管理 API オブジェクトとは異なります)
バージョン
このスキルは @trigger.dev/sdk に同梱されており、node_modules から直接読み込まれるため、インストール済みの SDK バージョンと常に一致します(隣接する package.json を参照)。これらの API の完全なドキュメントも、@trigger.dev/sdk/docs/ の下に同梱されています。