Restructured the realtime docs (#2317)
* Reordered react hooks + frontend sections * Updated the overview and nav * Separated out SWR hooks * Restructured metadata sections * Improved backend docs * Fixed broken link * Fixed broken links * Updated the structure * Restructured overview * Updated examples cards * Improved overview * Updated how it works * Updated auth * Added type safety to the run object page * made the subscribe description clearer * Fixed links in triggering * Fixed link * Removed examples footers * Copy tweak * Consolidated metadata and subscribe pages * moved metadata task examples to metadata * Fixed links * Fixing links like zelda * Removed dead import * Clearer titles * Cap R for Realtime * Fixes broken link --------- Co-authored-by: James Ritchie <james@trigger.dev>
This commit is contained in:
+63
-21
@@ -131,28 +131,30 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Frontend usage",
|
||||
"pages": [
|
||||
"frontend/overview",
|
||||
{
|
||||
"group": "React hooks",
|
||||
"pages": [
|
||||
"frontend/react-hooks/overview",
|
||||
"frontend/react-hooks/realtime",
|
||||
"frontend/react-hooks/triggering"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Realtime API",
|
||||
"group": "Realtime",
|
||||
"pages": [
|
||||
"realtime/overview",
|
||||
"realtime/streams",
|
||||
"realtime/react-hooks",
|
||||
"realtime/subscribe-to-run",
|
||||
"realtime/subscribe-to-runs-with-tag",
|
||||
"realtime/subscribe-to-batch"
|
||||
"realtime/how-it-works",
|
||||
"realtime/run-object",
|
||||
"realtime/auth",
|
||||
{
|
||||
"group": "React hooks (frontend)",
|
||||
"pages": [
|
||||
"realtime/react-hooks/overview",
|
||||
"realtime/react-hooks/triggering",
|
||||
"realtime/react-hooks/subscribe",
|
||||
"realtime/react-hooks/streams",
|
||||
"realtime/react-hooks/swr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Backend",
|
||||
"pages": [
|
||||
"realtime/backend/overview",
|
||||
"realtime/backend/subscribe",
|
||||
"realtime/backend/streams"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -621,7 +623,47 @@
|
||||
},
|
||||
{
|
||||
"source": "/frontend/react-hooks",
|
||||
"destination": "/frontend/react-hooks/overview"
|
||||
"destination": "/realtime/react-hooks/overview"
|
||||
},
|
||||
{
|
||||
"source": "/frontend/overview",
|
||||
"destination": "/realtime/auth"
|
||||
},
|
||||
{
|
||||
"source": "/frontend/react-hooks/overview",
|
||||
"destination": "/realtime/react-hooks/overview"
|
||||
},
|
||||
{
|
||||
"source": "/frontend/react-hooks/realtime",
|
||||
"destination": "/realtime/react-hooks/realtime"
|
||||
},
|
||||
{
|
||||
"source": "/frontend/react-hooks/triggering",
|
||||
"destination": "/realtime/react-hooks/triggering"
|
||||
},
|
||||
{
|
||||
"source": "/realtime/backend",
|
||||
"destination": "/realtime/backend/overview"
|
||||
},
|
||||
{
|
||||
"source": "/realtime/streams",
|
||||
"destination": "/realtime/backend/streams"
|
||||
},
|
||||
{
|
||||
"source": "/realtime/react-hooks",
|
||||
"destination": "/realtime/react-hooks/overview"
|
||||
},
|
||||
{
|
||||
"source": "/realtime/subscribe-to-run",
|
||||
"destination": "/realtime/backend/subscribe"
|
||||
},
|
||||
{
|
||||
"source": "/realtime/subscribe-to-runs-with-tag",
|
||||
"destination": "/realtime/backend/subscribe"
|
||||
},
|
||||
{
|
||||
"source": "/realtime/subscribe-to-batch",
|
||||
"destination": "/realtime/backend/subscribe"
|
||||
},
|
||||
{
|
||||
"source": "/management/projects/runs",
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
---
|
||||
title: Overview & Authentication
|
||||
sidebarTitle: Overview & Auth
|
||||
description: Using the Trigger.dev SDK from your frontend application.
|
||||
---
|
||||
|
||||
You can use our [React hooks](/frontend/react-hooks) in your frontend application to interact with the Trigger.dev API. This guide will show you how to generate Public Access Tokens that can be used to authenticate your requests.
|
||||
|
||||
## Authentication
|
||||
|
||||
To create a Public Access Token, you can use the `auth.createPublicToken` function in your **backend** code:
|
||||
|
||||
```tsx
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken(); // 👈 this public access token has no permissions, so is pretty useless!
|
||||
```
|
||||
|
||||
### Scopes
|
||||
|
||||
By default a Public Access Token has no permissions. You must specify the scopes you need when creating a Public Access Token:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
runs: true, // ❌ this token can read all runs, possibly useful for debugging/testing
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
This will allow the token to read all runs, which is probably not what you want. You can specify only certain runs by passing an array of run IDs:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
runs: ["run_1234", "run_5678"], // ✅ this token can read only these runs
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
You can scope the token to only read certain tasks:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
tasks: ["my-task-1", "my-task-2"], // 👈 this token can read all runs of these tasks
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Or tags:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
tags: ["my-tag-1", "my-tag-2"], // 👈 this token can read all runs with these tags
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Or a specific batch of runs:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
batch: "batch_1234", // 👈 this token can read all runs in this batch
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
You can also combine scopes. For example, to read runs with specific tags and for specific tasks:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
tasks: ["my-task-1", "my-task-2"],
|
||||
tags: ["my-tag-1", "my-tag-2"],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Expiration
|
||||
|
||||
By default, Public Access Token's expire after 15 minutes. You can specify a different expiration time when creating a Public Access Token:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
expirationTime: "1hr",
|
||||
});
|
||||
```
|
||||
|
||||
- If `expirationTime` is a string, it will be treated as a time span
|
||||
- If `expirationTime` is a number, it will be treated as a Unix timestamp
|
||||
- If `expirationTime` is a `Date`, it will be treated as a date
|
||||
|
||||
The format used for a time span is the same as the [jose package](https://github.com/panva/jose), which is a number followed by a unit. Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp.
|
||||
|
||||
## Auto-generated tokens
|
||||
|
||||
When triggering a task from your backend, the `handle` received from the `trigger` function now includes a `publicAccessToken` field. This token can be used to authenticate requests in your frontend application:
|
||||
|
||||
```ts
|
||||
import { tasks } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const handle = await tasks.trigger("my-task", { some: "data" });
|
||||
|
||||
console.log(handle.publicAccessToken);
|
||||
```
|
||||
|
||||
By default, tokens returned from the `trigger` function expire after 15 minutes and have a read scope for that specific run. You can customize the expiration of the auto-generated tokens by passing a `publicTokenOptions` object to the `trigger` function:
|
||||
|
||||
```ts
|
||||
import { tasks } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const handle = await tasks.trigger(
|
||||
"my-task",
|
||||
{ some: "data" },
|
||||
{
|
||||
tags: ["my-tag"],
|
||||
},
|
||||
{
|
||||
publicAccessToken: {
|
||||
expirationTime: "1hr",
|
||||
},
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
You will also get back a Public Access Token when using the `batchTrigger` function:
|
||||
|
||||
```ts
|
||||
import { tasks } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const handle = await tasks.batchTrigger("my-task", [
|
||||
{ payload: { some: "data" } },
|
||||
{ payload: { some: "data" } },
|
||||
{ payload: { some: "data" } },
|
||||
]);
|
||||
|
||||
console.log(handle.publicAccessToken);
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To learn how to use these Public Access Tokens, see our [React hooks](/frontend/react-hooks) guide.
|
||||
@@ -1,337 +0,0 @@
|
||||
---
|
||||
title: Overview
|
||||
sidebarTitle: Overview
|
||||
description: Using the Trigger.dev v3 API from your React application.
|
||||
---
|
||||
|
||||
import RealtimeExamplesCards from "/snippets/realtime-examples-cards.mdx";
|
||||
|
||||
Our react hooks package provides a set of hooks that make it easy to interact with the Trigger.dev API from your React application, using our [frontend API](/frontend/overview). You can use these hooks to fetch runs, and subscribe to real-time updates, and trigger tasks from your frontend application.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the `@trigger.dev/react-hooks` package in your project:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn install @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Authentication
|
||||
|
||||
All hooks accept an optional last argument `options` that accepts an `accessToken` param, which should be a valid Public Access Token. Learn more about [generating tokens in the frontend guide](/frontend/overview).
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken, // This is required
|
||||
baseURL: "https://your-trigger-dev-instance.com", // optional, only needed if you are self-hosting Trigger.dev
|
||||
});
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, you can use our `TriggerAuthContext` provider
|
||||
|
||||
```tsx
|
||||
import { TriggerAuthContext } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function SetupTrigger({ publicAccessToken }: { publicAccessToken: string }) {
|
||||
return (
|
||||
<TriggerAuthContext.Provider value={{ accessToken: publicAccessToken }}>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Now children components can use the hooks to interact with the Trigger.dev API. If you are self-hosting Trigger.dev, you can provide the `baseURL` to the `TriggerAuthContext` provider.
|
||||
|
||||
```tsx
|
||||
import { TriggerAuthContext } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function SetupTrigger({ publicAccessToken }: { publicAccessToken: string }) {
|
||||
return (
|
||||
<TriggerAuthContext.Provider
|
||||
value={{
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: "https://your-trigger-dev-instance.com",
|
||||
}}
|
||||
>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Next.js and client components
|
||||
|
||||
If you are using Next.js with the App Router, you have to make sure the component that uses the `TriggerAuthContext` is a client component. So for example, the following code will not work:
|
||||
|
||||
```tsx app/page.tsx
|
||||
import { TriggerAuthContext } from "@trigger.dev/react-hooks";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<TriggerAuthContext.Provider value={{ accessToken: "your-access-token" }}>
|
||||
<MyComponent />
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
That's because `Page` is a server component and the `TriggerAuthContext.Provider` uses client-only react code. To fix this, wrap the `TriggerAuthContext.Provider` in a client component:
|
||||
|
||||
```ts components/TriggerProvider.tsx
|
||||
"use client";
|
||||
|
||||
import { TriggerAuthContext } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function TriggerProvider({
|
||||
accessToken,
|
||||
children,
|
||||
}: {
|
||||
accessToken: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<TriggerAuthContext.Provider
|
||||
value={{
|
||||
accessToken,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</TriggerAuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Passing the token to the frontend
|
||||
|
||||
Techniques for passing the token to the frontend vary depending on your setup. Here are a few ways to do it for different setups:
|
||||
|
||||
#### Next.js App Router
|
||||
|
||||
If you are using Next.js with the App Router and you are triggering a task from a server action, you can use cookies to store and pass the token to the frontend.
|
||||
|
||||
```tsx actions/trigger.ts
|
||||
"use server";
|
||||
|
||||
import { tasks } from "@trigger.dev/sdk/v3";
|
||||
import type { exampleTask } from "@/trigger/example";
|
||||
import { redirect } from "next/navigation";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export async function startRun() {
|
||||
const handle = await tasks.trigger<typeof exampleTask>("example", { foo: "bar" });
|
||||
|
||||
// Set the auto-generated publicAccessToken in a cookie
|
||||
cookies().set("publicAccessToken", handle.publicAccessToken); // ✅ this token only has access to read this run
|
||||
|
||||
redirect(`/runs/${handle.id}`);
|
||||
}
|
||||
```
|
||||
|
||||
Then in the `/runs/[id].tsx` page, you can read the token from the cookie and pass it to the `TriggerProvider`.
|
||||
|
||||
```tsx pages/runs/[id].tsx
|
||||
import { TriggerProvider } from "@/components/TriggerProvider";
|
||||
|
||||
export default function RunPage({ params }: { params: { id: string } }) {
|
||||
const publicAccessToken = cookies().get("publicAccessToken");
|
||||
|
||||
return (
|
||||
<TriggerProvider accessToken={publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Instead of a cookie, you could also use a query parameter to pass the token to the frontend:
|
||||
|
||||
```tsx actions/trigger.ts
|
||||
import { tasks } from "@trigger.dev/sdk/v3";
|
||||
import type { exampleTask } from "@/trigger/example";
|
||||
import { redirect } from "next/navigation";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
export async function startRun() {
|
||||
const handle = await tasks.trigger<typeof exampleTask>("example", { foo: "bar" });
|
||||
|
||||
redirect(`/runs/${handle.id}?publicAccessToken=${handle.publicAccessToken}`);
|
||||
}
|
||||
```
|
||||
|
||||
And then in the `/runs/[id].tsx` page:
|
||||
|
||||
```tsx pages/runs/[id].tsx
|
||||
import { TriggerProvider } from "@/components/TriggerProvider";
|
||||
|
||||
export default function RunPage({
|
||||
params,
|
||||
searchParams,
|
||||
}: {
|
||||
params: { id: string };
|
||||
searchParams: { publicAccessToken: string };
|
||||
}) {
|
||||
return (
|
||||
<TriggerProvider accessToken={searchParams.publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Another alternative would be to use a server-side rendered page to fetch the token and pass it to the frontend:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```tsx pages/runs/[id].tsx
|
||||
import { TriggerProvider } from "@/components/TriggerProvider";
|
||||
import { generatePublicAccessToken } from "@/trigger/auth";
|
||||
|
||||
export default async function RunPage({ params }: { params: { id: string } }) {
|
||||
// This will be executed on the server only
|
||||
const publicAccessToken = await generatePublicAccessToken(params.id);
|
||||
|
||||
return (
|
||||
<TriggerProvider accessToken={publicAccessToken}>
|
||||
<RunDetails id={params.id} />
|
||||
</TriggerProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```tsx trigger/auth.ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
export async function generatePublicAccessToken(runId: string) {
|
||||
return auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
runs: [runId],
|
||||
},
|
||||
},
|
||||
expirationTime: "1h",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## SWR vs Realtime hooks
|
||||
|
||||
We offer two "styles" of hooks: SWR and Realtime. The SWR hooks use the [swr](https://swr.vercel.app/) library to fetch data once and cache it. The Realtime hooks use [Trigger.dev realtime](/realtime) to subscribe to updates in real-time.
|
||||
|
||||
<Note>
|
||||
It can be a little confusing which one to use because [swr](https://swr.vercel.app/) can also be
|
||||
configured to poll for updates. But because of rate-limits and the way the Trigger.dev API works,
|
||||
we recommend using the Realtime hooks for most use-cases.
|
||||
</Note>
|
||||
|
||||
## SWR Hooks
|
||||
|
||||
### useRun
|
||||
|
||||
The `useRun` hook allows you to fetch a run by its ID.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({ runId }: { runId: string }) {
|
||||
const { run, error, isLoading } = useRun(runId);
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
The `run` object returned is the same as the [run object](/management/runs/retrieve) returned by the Trigger.dev API. To correctly type the run's payload and output, you can provide the type of your task to the `useRun` hook:
|
||||
|
||||
```tsx
|
||||
import { useRun } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
export function MyComponent({ runId }: { runId: string }) {
|
||||
const { run, error, isLoading } = useRun<typeof myTask>(runId, {
|
||||
refreshInterval: 0, // Disable polling
|
||||
});
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### Common options
|
||||
|
||||
You can pass the following options to the all SWR hooks:
|
||||
|
||||
<ParamField path="revalidateOnFocus" type="boolean">
|
||||
Revalidate the data when the window regains focus.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="revalidateOnReconnect" type="boolean">
|
||||
Revalidate the data when the browser regains a network connection.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="refreshInterval" type="number">
|
||||
Poll for updates at the specified interval (in milliseconds). Polling is not recommended for most
|
||||
use-cases. Use the Realtime hooks instead.
|
||||
</ParamField>
|
||||
|
||||
### Common return values
|
||||
|
||||
<ResponseField name="error" type="Error">
|
||||
An error object if an error occurred while fetching the data.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isLoading" type="boolean">
|
||||
A boolean indicating if the data is currently being fetched.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isValidating" type="boolean">
|
||||
A boolean indicating if the data is currently being revalidated.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isError" type="boolean">
|
||||
A boolean indicating if an error occurred while fetching the data.
|
||||
</ResponseField>
|
||||
|
||||
## Realtime hooks
|
||||
|
||||
See our [Realtime hooks documentation](/frontend/react-hooks/realtime) for more information.
|
||||
|
||||
## Trigger Hooks
|
||||
|
||||
See our [Trigger hooks documentation](/frontend/react-hooks/triggering) for more information.
|
||||
|
||||
<RealtimeExamplesCards />
|
||||
@@ -1,420 +0,0 @@
|
||||
---
|
||||
title: Realtime hooks
|
||||
sidebarTitle: Realtime
|
||||
description: Get live updates from the Trigger.dev API in your frontend application.
|
||||
---
|
||||
|
||||
import RealtimeExamplesCards from "/snippets/realtime-examples-cards.mdx";
|
||||
|
||||
These hooks allow you to subscribe to runs, batches, and streams using [Trigger.dev realtime](/realtime). Before reading this guide:
|
||||
|
||||
- Read our [Realtime documentation](/realtime) to understand how the Trigger.dev realtime API works.
|
||||
- Read how to [setup and authenticate](/frontend/overview) using the `@trigger.dev/react-hooks` package.
|
||||
|
||||
## Hooks
|
||||
|
||||
### useRealtimeRun
|
||||
|
||||
The `useRealtimeRun` hook allows you to subscribe to a run by its ID.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
To correctly type the run's payload and output, you can provide the type of your task to the `useRealtimeRun` hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun<typeof myTask>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
You can supply an `onComplete` callback to the `useRealtimeRun` hook to be called when the run is completed or errored. This is useful if you want to perform some action when the run is completed, like navigating to a different page or showing a notification.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
onComplete: (run, error) => {
|
||||
console.log("Run completed", run);
|
||||
},
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
See our [Realtime documentation](/realtime) for more information about the type of the run object and more.
|
||||
|
||||
### useRealtimeRunsWithTag
|
||||
|
||||
The `useRealtimeRunsWithTag` hook allows you to subscribe to multiple runs with a specific tag.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
To correctly type the runs payload and output, you can provide the type of your task to the `useRealtimeRunsWithTag` hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag<typeof myTask>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now runs[i].payload and runs[i].output are correctly typed
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
If `useRealtimeRunsWithTag` could return multiple different types of tasks, you can pass a union of all the task types to the hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
import type { myTask1, myTask2 } from "@/trigger/myTasks";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag<typeof myTask1 | typeof myTask2>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// You can narrow down the type of the run based on the taskIdentifier
|
||||
for (const run of runs) {
|
||||
if (run.taskIdentifier === "my-task-1") {
|
||||
// run is correctly typed as myTask1
|
||||
} else if (run.taskIdentifier === "my-task-2") {
|
||||
// run is correctly typed as myTask2
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### useRealtimeBatch
|
||||
|
||||
The `useRealtimeBatch` hook allows you to subscribe to a batch of runs by its the batch ID.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeBatch } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({ batchId }: { batchId: string }) {
|
||||
const { runs, error } = useRealtimeBatch(batchId);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
See our [Realtime documentation](/realtime) for more information.
|
||||
|
||||
### useRealtimeRunWithStreams
|
||||
|
||||
The `useRealtimeRunWithStreams` hook allows you to subscribe to a run by its ID and also receive any streams that are emitted by the task. See our [Realtime documentation](/realtime#streams) for more information about emitting streams from a task.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, streams, error } = useRealtimeRunWithStreams(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>
|
||||
{Object.keys(streams).map((stream) => (
|
||||
<div key={stream}>Stream: {stream}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
You can provide the type of the streams to the `useRealtimeRunWithStreams` hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
type STREAMS = {
|
||||
openai: string; // this is the type of each "part" of the stream
|
||||
};
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, streams, error } = useRealtimeRunWithStreams<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const text = streams.openai?.map((part) => part).join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
As you can see above, each stream is an array of the type you provided, keyed by the stream name. If instead of a pure text stream you have a stream of objects, you can provide the type of the object:
|
||||
|
||||
```tsx
|
||||
import type { TextStreamPart } from "ai";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
type STREAMS = { openai: TextStreamPart<{}> };
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, streams, error } = useRealtimeRunWithStreams<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const text = streams.openai
|
||||
?.filter((stream) => stream.type === "text-delta")
|
||||
?.map((part) => part.text)
|
||||
.join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Common options
|
||||
|
||||
### accessToken & baseURL
|
||||
|
||||
You can pass the `accessToken` option to the Realtime hooks to authenticate the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: "https://my-self-hosted-trigger.com", // Optional if you are using a self-hosted Trigger.dev instance
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### enabled
|
||||
|
||||
You can pass the `enabled` option to the Realtime hooks to enable or disable the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
enabled,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
enabled: boolean;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
enabled,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to conditionally disable using the hook based on some state.
|
||||
|
||||
### id
|
||||
|
||||
You can pass the `id` option to the Realtime hooks to change the ID of the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
id,
|
||||
runId,
|
||||
publicAccessToken,
|
||||
enabled,
|
||||
}: {
|
||||
id: string;
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
enabled: boolean;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
enabled,
|
||||
id,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to change the ID of the subscription based on some state. Passing in a different ID will unsubscribe from the current subscription and subscribe to the new one (and remove any cached data).
|
||||
|
||||
### experimental_throttleInMs
|
||||
|
||||
The `*withStreams` variants of the Realtime hooks accept an `experimental_throttleInMs` option to throttle the updates from the server. This can be useful if you are getting too many updates and want to reduce the number of updates.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunsWithStreams } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { runs, error } = useRealtimeRunsWithStreams(tag, {
|
||||
accessToken: publicAccessToken,
|
||||
experimental_throttleInMs: 1000, // Throttle updates to once per second
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
<RealtimeExamplesCards />
|
||||
@@ -39,11 +39,11 @@ This demo is a full stack example that uses the following:
|
||||
- View the Trigger.dev task code in the [src/trigger/batch.ts](https://github.com/triggerdotdev/examples/blob/main/batch-llm-evaluator/src/trigger/batch.ts) file.
|
||||
- The `evaluateModels` task uses the `batch.triggerByTaskAndWait` method to distribute the task to the different LLM models.
|
||||
- It then passes the results through to a `summarizeEvals` task that calculates some dummy "tags" for each LLM response.
|
||||
- We use a [useRealtimeRunsWithTag](https://trigger.dev/docs/frontend/react-hooks/realtime#userealtimerunswithtag) hook to subscribe to the different evaluation tasks runs in the [src/components/llm-evaluator.tsx](https://github.com/triggerdotdev/examples/blob/main/batch-llm-evaluator/src/components/llm-evaluator.tsx) file.
|
||||
- We use a [useRealtimeRunsWithTag](/realtime/react-hooks/subscribe#userealtimerunswithtag) hook to subscribe to the different evaluation tasks runs in the [src/components/llm-evaluator.tsx](https://github.com/triggerdotdev/examples/blob/main/batch-llm-evaluator/src/components/llm-evaluator.tsx) file.
|
||||
- We then pass the relevant run down into three different components for the different models:
|
||||
- The `AnthropicEval` component: [src/components/evals/Anthropic.tsx](https://github.com/triggerdotdev/examples/blob/main/batch-llm-evaluator/src/components/evals/Anthropic.tsx)
|
||||
- The `XAIEval` component: [src/components/evals/XAI.tsx](https://github.com/triggerdotdev/examples/blob/main/batch-llm-evaluator/src/components/evals/XAI.tsx)
|
||||
- The `OpenAIEval` component: [src/components/evals/OpenAI.tsx](https://github.com/triggerdotdev/examples/blob/main/batch-llm-evaluator/src/components/evals/OpenAI.tsx)
|
||||
- Each of these components then uses [useRealtimeRunWithStreams](https://trigger.dev/docs/frontend/react-hooks/realtime#userealtimerunwithstreams) to subscribe to the different LLM responses.
|
||||
- Each of these components then uses [useRealtimeRunWithStreams](/realtime/react-hooks/streams) to subscribe to the different LLM responses.
|
||||
|
||||
<RealtimeLearnMore />
|
||||
|
||||
@@ -79,7 +79,6 @@ await wait.completeToken<ReviewPayload>(
|
||||
|
||||
<UpgradeToV4Note />
|
||||
|
||||
|
||||
While the workflow in this example is static and does not allow changing the connections between nodes in the UI, it serves as a good baseline for understanding how to build completely custom workflow builders using Trigger.dev and ReactFlow.
|
||||
|
||||
## Learn more about Trigger.dev Realtime and waitpoint tokens
|
||||
@@ -87,6 +86,6 @@ While the workflow in this example is static and does not allow changing the con
|
||||
To learn more, take a look at the following resources:
|
||||
|
||||
- [Trigger.dev Realtime](/realtime) - learn more about how to subscribe to runs and get real-time updates
|
||||
- [Realtime streaming](/realtime/streams) - learn more about streaming data from your tasks
|
||||
- [React hooks](/frontend/react-hooks) - learn more about using React hooks to interact with the Trigger.dev API
|
||||
- [Realtime streaming](/realtime/react-hooks/streams) - learn more about streaming data from your tasks
|
||||
- [React hooks](/realtime/react-hooks) - learn more about using React hooks to interact with the Trigger.dev API
|
||||
- [Waitpoint tokens](/wait-for-token) - learn about waitpoint tokens in Trigger.dev and human-in-the-loop flows
|
||||
|
||||
@@ -110,7 +110,7 @@ Level 0 (Initial Query): "AI safety in autonomous vehicles"
|
||||
|
||||
### Using Trigger.dev Realtime to trigger and subscribe to the deep research task
|
||||
|
||||
We use the [`useRealtimeTaskTrigger`](/frontend/react-hooks/triggering#userealtimetasktrigger) React hook to trigger the `deep-research` task and subscribe to it's updates.
|
||||
We use the [`useRealtimeTaskTrigger`](/realtime/react-hooks/triggering#userealtimetasktrigger) React hook to trigger the `deep-research` task and subscribe to it's updates.
|
||||
|
||||
**Frontend (React Hook)**:
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import RealtimeLearnMore from "/snippets/realtime-learn-more.mdx";
|
||||
This demo is a full stack example that uses the following:
|
||||
|
||||
- A [Next.js](https://nextjs.org/) app using [shadcn](https://ui.shadcn.com/) for the UI
|
||||
- Our 'useRealtimeRun' [React hook](https://trigger.dev/docs/frontend/react-hooks/realtime) to subscribe to the run and show updates on the frontend
|
||||
- Our 'useRealtimeRun' [React hook](/realtime/react-hooks/subscribe#userealtimerun) to subscribe to the run and show updates on the frontend
|
||||
- The [Vercel AI SDK](https://sdk.vercel.ai/docs/introduction) to [generate images](https://sdk.vercel.ai/docs/ai-sdk-core/image-generation) using OpenAI's DALL-E models
|
||||
|
||||
## GitHub repo
|
||||
@@ -36,6 +36,6 @@ This demo is a full stack example that uses the following:
|
||||
## Relevant code
|
||||
|
||||
- View the Trigger.dev task code which generates the image using the Vercel AI SDK in [src/trigger/realtime-generate-image.ts](https://github.com/triggerdotdev/examples/tree/main/vercel-ai-sdk-image-generator/src/trigger/realtime-generate-image.ts).
|
||||
- We use a [useRealtimeRun](https://trigger.dev/docs/frontend/react-hooks/realtime#userealtimerun) hook to subscribe to the run in [src/app/processing/[id]/ProcessingContent.tsx](https://github.com/triggerdotdev/examples/tree/main/vercel-ai-sdk-image-generator/src/app/processing/[id]/ProcessingContent.tsx).
|
||||
- We use a [useRealtimeRun](/realtime/react-hooks/subscribe#userealtimerun) hook to subscribe to the run in [src/app/processing/[id]/ProcessingContent.tsx](https://github.com/triggerdotdev/examples/tree/main/vercel-ai-sdk-image-generator/src/app/processing/[id]/ProcessingContent.tsx).
|
||||
|
||||
<RealtimeLearnMore />
|
||||
|
||||
+15
-5
@@ -12,7 +12,11 @@ mode: "center"
|
||||
<Card title="Examples" img="/images/intro-examples.jpg" href="/guides/introduction#example-tasks">
|
||||
Explore dozens of examples tasks to use in your own projects
|
||||
</Card>
|
||||
<Card title="Frameworks" img="/images/intro-frameworks.jpg" href="/guides/introduction#frameworks">
|
||||
<Card
|
||||
title="Frameworks"
|
||||
img="/images/intro-frameworks.jpg"
|
||||
href="/guides/introduction#frameworks"
|
||||
>
|
||||
Learn how to use Trigger.dev with your favorite frameworks
|
||||
</Card>
|
||||
<Card title="Video walkthrough" img="/images/intro-video.jpg" href="/video-walkthrough">
|
||||
@@ -24,7 +28,7 @@ mode: "center"
|
||||
|
||||
Trigger.dev is an open source background jobs framework that lets you write reliable workflows in plain async code. Run long-running AI tasks, handle complex background jobs, and build AI agents with built-in queuing, automatic retries, and real-time monitoring. No timeouts, elastic scaling, and zero infrastructure management required.
|
||||
|
||||
We provide everything you need to build and manage background tasks: a CLI and SDK for writing tasks in your existing codebase, support for both [regular](/tasks/overview) and [scheduled](/tasks/scheduled) tasks, full observability through our dashboard, and a [Realtime API](/realtime) with [React hooks](/frontend/react-hooks#realtime-hooks) for showing task status in your frontend. You can use [Trigger.dev Cloud](https://cloud.trigger.dev) or [self-host](/open-source-self-hosting) on your own infrastructure.
|
||||
We provide everything you need to build and manage background tasks: a CLI and SDK for writing tasks in your existing codebase, support for both [regular](/tasks/overview) and [scheduled](/tasks/scheduled) tasks, full observability through our dashboard, and a [Realtime API](/realtime) with [React hooks](/realtime/react-hooks#realtime-hooks) for showing task status in your frontend. You can use [Trigger.dev Cloud](https://cloud.trigger.dev) or [self-host](/open-source-self-hosting) on your own infrastructure.
|
||||
|
||||
## Learn the concepts
|
||||
|
||||
@@ -39,7 +43,8 @@ We provide everything you need to build and manage background tasks: a CLI and S
|
||||
Runs are the instances of tasks that are executed. Learn how they work.
|
||||
</Card>
|
||||
<Card title="API keys" icon="key" href="/apikeys" color="#EAEA08">
|
||||
API keys are used to authenticate requests to the Trigger.dev API. Learn how to create and use them.
|
||||
API keys are used to authenticate requests to the Trigger.dev API. Learn how to create and use
|
||||
them.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -52,13 +57,18 @@ We provide everything you need to build and manage background tasks: a CLI and S
|
||||
<Card title="Realtime API" icon="loader" href="/realtime" color="#22C55E">
|
||||
The Realtime API allows you to trigger tasks and get the status of runs.
|
||||
</Card>
|
||||
<Card title="React hooks" icon="react" href="/frontend/react-hooks" color="#3B82F6">
|
||||
<Card title="React hooks" icon="react" href="/realtime/react-hooks" color="#3B82F6">
|
||||
React hooks are a way to show task status in your frontend.
|
||||
</Card>
|
||||
<Card title="Waits" icon="calendar-clock" href="/wait" color="#F59E0B">
|
||||
Waits are a way to wait for a task to finish before continuing.
|
||||
</Card>
|
||||
<Card title="Errors and retries" icon="message-exclamation" href="/errors-retrying" color="#F43F5E">
|
||||
<Card
|
||||
title="Errors and retries"
|
||||
icon="message-exclamation"
|
||||
href="/errors-retrying"
|
||||
color="#F43F5E"
|
||||
>
|
||||
Learn how to handle errors and retries.
|
||||
</Card>
|
||||
<Card title="Concurrency & Queues" icon="line-height" href="/queue-concurrency" color="#D946EF">
|
||||
|
||||
@@ -8,7 +8,7 @@ There are two methods of authenticating with the management API: using a secret
|
||||
|
||||
<Note>
|
||||
There is a separate authentication strategy when making requests from your frontend application.
|
||||
See the [Frontend guide](/frontend/overview) for more information. This guide is for backend usage
|
||||
See the [Realtime guide](/realtime/overview) for more information. This guide is for backend usage
|
||||
only.
|
||||
</Note>
|
||||
|
||||
@@ -94,4 +94,4 @@ await envvars.upload("proj_1234", "dev", {
|
||||
},
|
||||
override: true,
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
@@ -296,7 +296,7 @@ export function TaskStatus({
|
||||
}
|
||||
```
|
||||
|
||||
For more information, see the [React Hooks documentation](/frontend/react-hooks/overview).
|
||||
For more information, see the [React Hooks documentation](/realtime/react-hooks/overview).
|
||||
|
||||
## Build extensions
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
---
|
||||
title: Realtime authentication
|
||||
sidebarTitle: Realtime auth
|
||||
description: Authenticating real-time API requests with Public Access Tokens or Trigger Tokens
|
||||
---
|
||||
|
||||
To use the Realtime API, you need to authenticate your requests with Public Access Tokens or Trigger Tokens. These tokens provide secure, scoped access to your runs and can be used in both frontend and backend applications.
|
||||
|
||||
## Token Types
|
||||
|
||||
There are two types of tokens you can use with the Realtime API:
|
||||
|
||||
- **[Public Access Tokens](#public-access-tokens-for-subscribing-to-runs)** - Used to read and subscribe to run data. Can be used in both the frontend and backend.
|
||||
- **[Trigger Tokens](#trigger-tokens-for-frontend-triggering-only)** - Used to trigger tasks from your frontend. These are more secure, single-use tokens that can only be used in the frontend.
|
||||
|
||||
## Public Access Tokens (for subscribing to runs)
|
||||
|
||||
Use Public Access Tokens to subscribe to runs and receive real-time updates in your frontend or backend.
|
||||
|
||||
### Creating Public Access Tokens
|
||||
|
||||
You can create a Public Access Token using the `auth.createPublicToken` function in your **backend** code:
|
||||
|
||||
```tsx
|
||||
// Somewhere in your backend code
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken(); // 👈 this public access token has no permissions, so is pretty useless!
|
||||
```
|
||||
|
||||
### Scopes
|
||||
|
||||
By default a Public Access Token has no permissions. You must specify the scopes you need when creating a Public Access Token:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
runs: true, // ❌ this token can read all runs, possibly useful for debugging/testing
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
This will allow the token to read all runs, which is probably not what you want. You can specify only certain runs by passing an array of run IDs:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
runs: ["run_1234", "run_5678"], // ✅ this token can read only these runs
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
You can scope the token to only read certain tasks:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
tasks: ["my-task-1", "my-task-2"], // 👈 this token can read all runs of these tasks
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Or tags:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
tags: ["my-tag-1", "my-tag-2"], // 👈 this token can read all runs with these tags
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Or a specific batch of runs:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
batch: "batch_1234", // 👈 this token can read all runs in this batch
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
You can also combine scopes. For example, to read runs with specific tags and for specific tasks:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
tasks: ["my-task-1", "my-task-2"],
|
||||
tags: ["my-tag-1", "my-tag-2"],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Expiration
|
||||
|
||||
By default, Public Access Token's expire after 15 minutes. You can specify a different expiration time when creating a Public Access Token:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
expirationTime: "1hr",
|
||||
});
|
||||
```
|
||||
|
||||
- If `expirationTime` is a string, it will be treated as a time span
|
||||
- If `expirationTime` is a number, it will be treated as a Unix timestamp
|
||||
- If `expirationTime` is a `Date`, it will be treated as a date
|
||||
|
||||
The format used for a time span is the same as the [jose package](https://github.com/panva/jose), which is a number followed by a unit. Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an alias for a year. If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets subtracted from the current unix timestamp. A "from now" suffix can also be used for readability when adding to the current unix timestamp.
|
||||
|
||||
### Auto-generated tokens
|
||||
|
||||
When you [trigger tasks](/triggering) from your backend, the `handle` received includes a `publicAccessToken` field. This token can be used to authenticate real-time requests in your frontend application.
|
||||
|
||||
By default, auto-generated tokens expire after 15 minutes and have a read scope for the specific run(s) that were triggered. You can customize the expiration by passing a `publicTokenOptions` object to the trigger function.
|
||||
|
||||
See our [triggering documentation](/triggering) for detailed examples of how to trigger tasks and get auto-generated tokens.
|
||||
|
||||
### Subscribing to runs with Public Access Tokens
|
||||
|
||||
Once you have a Public Access Token, you can use it to authenticate requests to the Realtime API in both backend and frontend applications.
|
||||
|
||||
**Backend usage:** See our [backend documentation](/realtime/backend) for examples of what you can do with Realtime in your backend once you have authenticated with a token.
|
||||
|
||||
**Frontend usage:** See our [React hooks documentation](/realtime/react-hooks) for examples of using tokens with frontend components.
|
||||
|
||||
## Trigger Tokens (for frontend triggering only)
|
||||
|
||||
For triggering tasks from your frontend, you need special "trigger" tokens. These tokens can only be used once to trigger a task and are more secure than regular Public Access Tokens.
|
||||
|
||||
### Creating Trigger Tokens
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
const triggerToken = await auth.createTriggerPublicToken("my-task");
|
||||
```
|
||||
|
||||
### Multiple tasks
|
||||
|
||||
You can pass multiple tasks to create a token that can trigger multiple tasks:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
const triggerToken = await auth.createTriggerPublicToken(["my-task-1", "my-task-2"]);
|
||||
```
|
||||
|
||||
### Multiple use
|
||||
|
||||
You can also create tokens that can be used multiple times:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
const triggerToken = await auth.createTriggerPublicToken("my-task", {
|
||||
multipleUse: true, // ❌ Use this with caution!
|
||||
});
|
||||
```
|
||||
|
||||
### Expiration
|
||||
|
||||
These tokens also expire, with the default expiration time being 15 minutes. You can specify a custom expiration time:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
const triggerToken = await auth.createTriggerPublicToken("my-task", {
|
||||
expirationTime: "24hr",
|
||||
});
|
||||
```
|
||||
|
||||
### Triggering tasks from the frontend with Trigger Tokens
|
||||
|
||||
Check out our [React hooks documentation](/realtime/react-hooks) for examples of how to use Trigger Tokens in your frontend applications.
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Backend overview
|
||||
sidebarTitle: Overview
|
||||
description: Using the Trigger.dev realtime API from your backend code
|
||||
---
|
||||
|
||||
import RealtimeExamplesCards from "/snippets/realtime-examples-cards.mdx";
|
||||
|
||||
Use these backend functions to subscribe to runs and streams from your server-side code or other tasks.
|
||||
|
||||
## Overview
|
||||
|
||||
There are three main categories of functionality:
|
||||
|
||||
- **[Subscribe functions](/realtime/backend/subscribe)** - Subscribe to run updates using async iterators
|
||||
- **[Metadata](/realtime/backend/subscribe#subscribe-to-metadata-updates-from-your-tasks)** - Update and subscribe to run metadata in real-time
|
||||
- **[Streams](/realtime/backend/streams)** - Emit and consume real-time streaming data from your tasks
|
||||
|
||||
## Authentication
|
||||
|
||||
All backend functions support both server-side and client-side authentication:
|
||||
|
||||
- **Server-side**: Use your API key (automatically handled in tasks)
|
||||
- **Client-side**: Generate a Public Access Token with appropriate scopes
|
||||
|
||||
See our [authentication guide](/realtime/auth) for detailed information on creating and using tokens.
|
||||
|
||||
## Quick example
|
||||
|
||||
Subscribe to a run:
|
||||
|
||||
```ts
|
||||
import { runs, tasks } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Trigger a task
|
||||
const handle = await tasks.trigger("my-task", { some: "data" });
|
||||
|
||||
// Subscribe to real-time updates
|
||||
for await (const run of runs.subscribeToRun(handle.id)) {
|
||||
console.log(`Run ${run.id} status: ${run.status}`);
|
||||
}
|
||||
```
|
||||
@@ -1,20 +1,23 @@
|
||||
---
|
||||
title: Realtime streams
|
||||
title: Streams
|
||||
sidebarTitle: Streams
|
||||
description: Stream data in realtime from inside your tasks
|
||||
description: Emit and consume real-time streaming data from your tasks
|
||||
---
|
||||
|
||||
import RealtimeExamplesCards from "/snippets/realtime-examples-cards.mdx";
|
||||
The Streams API allows you to stream data from your tasks to the outside world in realtime using the [metadata](/runs/metadata) system. This is particularly useful for streaming LLM outputs or any other real-time data.
|
||||
|
||||
The world is going realtime, and so should your tasks. With the Streams API, you can stream data from your tasks to the outside world in realtime. This is useful for a variety of use cases, including AI.
|
||||
<Note>
|
||||
For frontend applications using React, see our [React hooks streams
|
||||
documentation](/realtime/react-hooks/streams) for consuming streams in your UI.
|
||||
</Note>
|
||||
|
||||
## How it works
|
||||
## How streams work
|
||||
|
||||
The Streams API is a simple API that allows you to send data from your tasks to the outside world in realtime using the [metadata](/runs/metadata) system. You can send any kind of data that is streamed in realtime, but the most common use case is to send streaming output from streaming LLM providers, like OpenAI.
|
||||
Streams use the metadata system to send data chunks in real-time. You register a stream with a specific key using `metadata.stream`, and then consumers can subscribe to receive those chunks as they're emitted.
|
||||
|
||||
## Usage
|
||||
## Basic streaming example
|
||||
|
||||
To use the Streams API, you need to register a stream with a specific key using `metadata.stream`. The following example uses the OpenAI SDK with `stream: true` to stream the output of the LLM model in realtime:
|
||||
Here's how to stream data from OpenAI in your task:
|
||||
|
||||
```ts
|
||||
import { task, metadata } from "@trigger.dev/sdk/v3";
|
||||
@@ -56,12 +59,9 @@ export const myTask = task({
|
||||
});
|
||||
```
|
||||
|
||||
You can then subscribe to the stream using the `runs.subscribeToRun` method:
|
||||
## Subscribing to streams from backend
|
||||
|
||||
<Note>
|
||||
`runs.subscribeToRun` should be used from your backend or another task. To subscribe to a run from
|
||||
your frontend, you can use our [React hooks](/frontend/react-hooks).
|
||||
</Note>
|
||||
You can subscribe to the stream using the `runs.subscribeToRun` method with `.withStreams()`:
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
@@ -86,7 +86,9 @@ async function subscribeToStream(runId: string) {
|
||||
}
|
||||
```
|
||||
|
||||
You can register and subscribe to multiple streams in the same task. Let's add a stream from the response body of a fetch request:
|
||||
## Multiple streams
|
||||
|
||||
You can register and subscribe to multiple streams in the same task:
|
||||
|
||||
```ts
|
||||
import { task, metadata } from "@trigger.dev/sdk/v3";
|
||||
@@ -97,7 +99,7 @@ const openai = new OpenAI({
|
||||
});
|
||||
|
||||
export type STREAMS = {
|
||||
openai: OpenAI.ChatCompletionChunk; // The type of the chunk is determined by the provider
|
||||
openai: OpenAI.ChatCompletionChunk;
|
||||
fetch: string; // The response body will be an array of strings
|
||||
};
|
||||
|
||||
@@ -110,7 +112,7 @@ export const myTask = task({
|
||||
stream: true,
|
||||
});
|
||||
|
||||
// Register the stream with the key "openai"
|
||||
// Register the OpenAI stream
|
||||
await metadata.stream("openai", completion);
|
||||
|
||||
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
|
||||
@@ -119,7 +121,7 @@ export const myTask = task({
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the stream with the key "fetch"
|
||||
// Register a fetch response stream
|
||||
// Pipe the response.body through a TextDecoderStream to convert it to a string
|
||||
await metadata.stream("fetch", response.body.pipeThrough(new TextDecoderStream()));
|
||||
},
|
||||
@@ -133,7 +135,7 @@ export const myTask = task({
|
||||
task.
|
||||
</Note>
|
||||
|
||||
And then subscribing to the streams:
|
||||
Then subscribe to both streams:
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
@@ -141,7 +143,6 @@ import type { myTask, STREAMS } from "./trigger/my-task";
|
||||
|
||||
// Somewhere in your backend
|
||||
async function subscribeToStream(runId: string) {
|
||||
// Use a for-await loop to subscribe to the stream
|
||||
for await (const part of runs.subscribeToRun<typeof myTask>(runId).withStreams<STREAMS>()) {
|
||||
switch (part.type) {
|
||||
case "run": {
|
||||
@@ -163,45 +164,9 @@ async function subscribeToStream(runId: string) {
|
||||
}
|
||||
```
|
||||
|
||||
## React hooks
|
||||
## Using with the AI SDK
|
||||
|
||||
If you're building a frontend application, you can use our React hooks to subscribe to streams. Here's an example of how you can use the `useRealtimeRunWithStreams` hook to subscribe to a stream:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
import type { myTask, STREAMS } from "./trigger/my-task";
|
||||
|
||||
// Somewhere in your React component
|
||||
function MyComponent({ runId, publicAccessToken }: { runId: string; publicAccessToken: string }) {
|
||||
const { run, streams } = useRealtimeRunWithStreams<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (!run) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Run ID: {run.id}</h1>
|
||||
<h2>Streams:</h2>
|
||||
<ul>
|
||||
{Object.entries(streams).map(([key, value]) => (
|
||||
<li key={key}>
|
||||
<strong>{key}</strong>: {JSON.stringify(value)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Read more about using the React hooks in the [React hooks](/frontend/react-hooks) documentation.
|
||||
|
||||
## Usage with the `ai` SDK
|
||||
|
||||
The [ai SDK](https://sdk.vercel.ai/docs/introduction) provides a higher-level API for working with AI models. You can use the `ai` SDK with the Streams API by using the `streamText` method:
|
||||
The [AI SDK](https://sdk.vercel.ai/docs/introduction) provides a higher-level API for working with AI models. You can use it with the Streams API:
|
||||
|
||||
```ts
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
@@ -244,35 +209,9 @@ export const aiStreaming = schemaTask({
|
||||
});
|
||||
```
|
||||
|
||||
And then render the stream in your frontend:
|
||||
## Using AI SDK with tools
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
import type { aiStreaming, STREAMS } from "./trigger/ai-streaming";
|
||||
|
||||
function MyComponent({ runId, publicAccessToken }: { runId: string; publicAccessToken: string }) {
|
||||
const { streams } = useRealtimeRunWithStreams<typeof aiStreaming, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (!streams.openai) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
const text = streams.openai.join(""); // `streams.openai` is an array of strings
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>OpenAI response:</h2>
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Using tools and `fullStream`
|
||||
|
||||
When calling `streamText`, you can provide a `tools` object that allows the LLM to use additional tools. You can then access the tool call and results using the `fullStream` method:
|
||||
When using tools with the AI SDK, you can access tool calls and results using the `fullStream`:
|
||||
|
||||
```ts
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
@@ -306,7 +245,7 @@ export const aiStreamingWithTools = schemaTask({
|
||||
prompt: z
|
||||
.string()
|
||||
.default(
|
||||
"Based on the temperature, will I need to wear extra clothes today in San Fransico? Please be detailed."
|
||||
"Based on the temperature, will I need to wear extra clothes today in San Francisco? Please be detailed."
|
||||
),
|
||||
}),
|
||||
run: async ({ model, prompt }) => {
|
||||
@@ -338,50 +277,9 @@ export const aiStreamingWithTools = schemaTask({
|
||||
});
|
||||
```
|
||||
|
||||
Now you can get access to the tool call and results in your frontend:
|
||||
## Using toolTask
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
import type { aiStreamingWithTools, STREAMS } from "./trigger/ai-streaming";
|
||||
|
||||
function MyComponent({ runId, publicAccessToken }: { runId: string; publicAccessToken: string }) {
|
||||
const { streams } = useRealtimeRunWithStreams<typeof aiStreamingWithTools, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (!streams.openai) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
// streams.openai is an array of TextStreamPart
|
||||
const toolCall = streams.openai.find(
|
||||
(stream) => stream.type === "tool-call" && stream.toolName === "getWeather"
|
||||
);
|
||||
const toolResult = streams.openai.find((stream) => stream.type === "tool-result");
|
||||
const textDeltas = streams.openai.filter((stream) => stream.type === "text-delta");
|
||||
|
||||
const text = textDeltas.map((delta) => delta.textDelta).join("");
|
||||
const weatherLocation = toolCall ? toolCall.args.location : undefined;
|
||||
const weather = toolResult ? toolResult.result.temperature : undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>OpenAI response:</h2>
|
||||
<p>{text}</p>
|
||||
<h2>Weather:</h2>
|
||||
<p>
|
||||
{weatherLocation
|
||||
? `The weather in ${weatherLocation} is ${weather} degrees.`
|
||||
: "No weather data"}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Using `toolTask`
|
||||
|
||||
As you can see above, we defined a tool which will be used in the `aiStreamingWithTools` task. You can also define a Trigger.dev task that can be used as a tool, and will automatically be invoked with `triggerAndWait` when the tool is called. This is done using the `toolTask` function:
|
||||
You can define a Trigger.dev task that can be used as a tool, and will automatically be invoked with `triggerAndWait` when the tool is called:
|
||||
|
||||
```ts
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
@@ -418,7 +316,7 @@ export const aiStreamingWithTools = schemaTask({
|
||||
prompt: z
|
||||
.string()
|
||||
.default(
|
||||
"Based on the temperature, will I need to wear extra clothes today in San Fransico? Please be detailed."
|
||||
"Based on the temperature, will I need to wear extra clothes today in San Francisco? Please be detailed."
|
||||
),
|
||||
}),
|
||||
run: async ({ model, prompt }) => {
|
||||
@@ -451,5 +349,3 @@ export const aiStreamingWithTools = schemaTask({
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<RealtimeExamplesCards />
|
||||
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: Subscribe functions
|
||||
sidebarTitle: Subscribe
|
||||
description: Subscribe to run updates using async iterators
|
||||
---
|
||||
|
||||
These functions allow you to subscribe to run updates from your backend code. Each function returns an async iterator that yields run objects as they change.
|
||||
|
||||
## runs.subscribeToRun
|
||||
|
||||
Subscribes to all changes to a specific run.
|
||||
|
||||
```ts Example
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
for await (const run of runs.subscribeToRun("run_1234")) {
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
This function subscribes to all changes to a run. It returns an async iterator that yields the run object whenever the run is updated. The iterator will complete when the run is finished.
|
||||
|
||||
**Authentication**: This function supports both server-side and client-side authentication. For server-side authentication, use your API key. For client-side authentication, you must generate a public access token with read access to the specific run. See our [authentication guide](/realtime/auth) for details.
|
||||
|
||||
**Response**: The AsyncIterator yields the [run object](/realtime/run-object).
|
||||
|
||||
## runs.subscribeToRunsWithTag
|
||||
|
||||
Subscribes to all changes to runs with a specific tag.
|
||||
|
||||
```ts Example
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
for await (const run of runs.subscribeToRunsWithTag("user:1234")) {
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
This function subscribes to all changes to runs with a specific tag. It returns an async iterator that yields the run object whenever a run with the specified tag is updated. This iterator will never complete, so you must manually break out of the loop when you no longer want to receive updates.
|
||||
|
||||
**Authentication**: This function supports both server-side and client-side authentication. For server-side authentication, use your API key. For client-side authentication, you must generate a public access token with read access to the specific tag. See our [authentication guide](/realtime/auth) for details.
|
||||
|
||||
**Response**: The AsyncIterator yields the [run object](/realtime/run-object).
|
||||
|
||||
## runs.subscribeToBatch
|
||||
|
||||
Subscribes to all changes for runs in a batch.
|
||||
|
||||
```ts Example
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
for await (const run of runs.subscribeToBatch("batch_1234")) {
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
This function subscribes to all changes for runs in a batch. It returns an async iterator that yields a run object whenever a run in the batch is updated. The iterator does not complete on its own, you must manually `break` the loop when you want to stop listening for updates.
|
||||
|
||||
**Authentication**: This function supports both server-side and client-side authentication. For server-side authentication, use your API key. For client-side authentication, you must generate a public access token with read access to the specific batch. See our [authentication guide](/realtime/auth) for details.
|
||||
|
||||
**Response**: The AsyncIterator yields the [run object](/realtime/run-object).
|
||||
|
||||
## Type safety
|
||||
|
||||
You can infer the types of the run's payload and output by passing the type of the task to the subscribe functions:
|
||||
|
||||
```ts
|
||||
import { runs, tasks } from "@trigger.dev/sdk/v3";
|
||||
import type { myTask } from "./trigger/my-task";
|
||||
|
||||
async function myBackend() {
|
||||
const handle = await tasks.trigger("my-task", { some: "data" });
|
||||
|
||||
for await (const run of runs.subscribeToRun<typeof myTask>(handle.id)) {
|
||||
// run.payload and run.output are now typed
|
||||
console.log(run.payload.some);
|
||||
|
||||
if (run.output) {
|
||||
console.log(run.output.some);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When using `subscribeToRunsWithTag`, you can pass a union of task types:
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
import type { myTask, myOtherTask } from "./trigger/my-task";
|
||||
|
||||
for await (const run of runs.subscribeToRunsWithTag<typeof myTask | typeof myOtherTask>("my-tag")) {
|
||||
// Narrow down the type based on the taskIdentifier
|
||||
switch (run.taskIdentifier) {
|
||||
case "my-task": {
|
||||
console.log("Run output:", run.output.foo); // Type-safe
|
||||
break;
|
||||
}
|
||||
case "my-other-task": {
|
||||
console.log("Run output:", run.output.bar); // Type-safe
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Subscribe to metadata updates from your tasks
|
||||
|
||||
The metadata API allows you to update custom metadata on runs and receive real-time updates when metadata changes. This is useful for tracking progress, storing intermediate results, or adding custom status information that can be monitored in real-time.
|
||||
|
||||
<Note>
|
||||
For frontend applications using React, see our [React hooks metadata
|
||||
documentation](/realtime/react-hooks/subscribe#using-metadata-to-show-progress-in-your-ui) for
|
||||
consuming metadata updates in your UI.
|
||||
</Note>
|
||||
|
||||
When you update metadata from within a task using `metadata.set()`, `metadata.append()`, or other metadata methods, all subscribers to that run will automatically receive the updated run object containing the new metadata.
|
||||
|
||||
This makes metadata perfect for:
|
||||
|
||||
- Progress tracking
|
||||
- Status updates
|
||||
- Intermediate results
|
||||
- Custom notifications
|
||||
|
||||
Use the metadata API within your task to update metadata in real-time. In this basic example task, we're updating the progress of a task as it processes items.
|
||||
|
||||
### How to subscribe to metadata updates
|
||||
|
||||
This example task updates the progress of a task as it processes items.
|
||||
|
||||
```ts
|
||||
// Your task code
|
||||
import { task, metadata } from "@trigger.dev/sdk/v3";
|
||||
|
||||
export const progressTask = task({
|
||||
id: "progress-task",
|
||||
run: async (payload: { items: string[] }) => {
|
||||
const total = payload.items.length;
|
||||
|
||||
for (let i = 0; i < payload.items.length; i++) {
|
||||
// Update progress metadata
|
||||
metadata.set("progress", {
|
||||
current: i + 1,
|
||||
total: total,
|
||||
percentage: Math.round(((i + 1) / total) * 100),
|
||||
currentItem: payload.items[i],
|
||||
});
|
||||
|
||||
// Process the item
|
||||
await processItem(payload.items[i]);
|
||||
}
|
||||
|
||||
metadata.set("status", "completed");
|
||||
return { processed: total };
|
||||
},
|
||||
});
|
||||
|
||||
async function processItem(item: string) {
|
||||
// Simulate work
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
}
|
||||
```
|
||||
|
||||
We can now subscribe to the runs and receive real-time metadata updates.
|
||||
|
||||
```ts
|
||||
// Somewhere in your backend code
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
import type { progressTask } from "./trigger/progress-task";
|
||||
|
||||
async function monitorProgress(runId: string) {
|
||||
for await (const run of runs.subscribeToRun<typeof progressTask>(runId)) {
|
||||
console.log(`Run ${run.id} status: ${run.status}`);
|
||||
|
||||
if (run.metadata?.progress) {
|
||||
const progress = run.metadata.progress as {
|
||||
current: number;
|
||||
total: number;
|
||||
percentage: number;
|
||||
currentItem: string;
|
||||
};
|
||||
|
||||
console.log(`Progress: ${progress.current}/${progress.total} (${progress.percentage}%)`);
|
||||
console.log(`Processing: ${progress.currentItem}`);
|
||||
}
|
||||
|
||||
if (run.metadata?.status === "completed") {
|
||||
console.log("Task completed!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more information on how to write tasks that use the metadata API, as well as more examples, see our [run metadata docs](/runs/metadata#more-metadata-task-examples).
|
||||
|
||||
### Type safety
|
||||
|
||||
You can get type safety for your metadata by defining types:
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
import type { progressTask } from "./trigger/progress-task";
|
||||
|
||||
interface ProgressMetadata {
|
||||
progress?: {
|
||||
current: number;
|
||||
total: number;
|
||||
percentage: number;
|
||||
currentItem: string;
|
||||
};
|
||||
status?: "running" | "completed" | "failed";
|
||||
}
|
||||
|
||||
async function monitorTypedProgress(runId: string) {
|
||||
for await (const run of runs.subscribeToRun<typeof progressTask>(runId)) {
|
||||
const metadata = run.metadata as ProgressMetadata;
|
||||
|
||||
if (metadata?.progress) {
|
||||
// Now you have full type safety
|
||||
console.log(`Progress: ${metadata.progress.percentage}%`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: How it works
|
||||
sidebarTitle: How it works
|
||||
description: Technical details about how the Trigger.dev Realtime API works
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
The Realtime API is built on top of [Electric SQL](https://electric-sql.com/), an open-source PostgreSQL syncing engine. The Trigger.dev API wraps Electric SQL and provides a simple API to subscribe to [runs](/runs) and get real-time updates.
|
||||
|
||||
## Run changes
|
||||
|
||||
You will receive updates whenever a run changes for the following reasons:
|
||||
|
||||
- The run moves to a new state. See our [run lifecycle docs](/runs#the-run-lifecycle) for more information.
|
||||
- [Run tags](/tags) are added or removed.
|
||||
- [Run metadata](/runs/metadata) is updated.
|
||||
|
||||
## Run object
|
||||
|
||||
The run object returned by Realtime subscriptions is optimized for streaming updates and differs from the management API's run object. See [the run object](/realtime/run-object) page for the complete schema and field descriptions.
|
||||
|
||||
## Basic usage
|
||||
|
||||
After you trigger a task, you can subscribe to the run using the `runs.subscribeToRun` function. This function returns an async iterator that you can use to get updates on the run status.
|
||||
|
||||
```ts
|
||||
import { runs, tasks } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
async function myBackend() {
|
||||
const handle = await tasks.trigger("my-task", { some: "data" });
|
||||
|
||||
for await (const run of runs.subscribeToRun(handle.id)) {
|
||||
// This will log the run every time it changes
|
||||
console.log(run);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Every time the run changes, the async iterator will yield the updated run. You can use this to update your UI, log the run status, or take any other action.
|
||||
|
||||
Alternatively, you can subscribe to changes to any run that includes a specific tag (or tags) using the `runs.subscribeToRunsWithTag` function.
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
for await (const run of runs.subscribeToRunsWithTag("user:1234")) {
|
||||
// This will log the run every time it changes, for all runs with the tag "user:1234"
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
If you've used `batchTrigger` to trigger multiple runs, you can also subscribe to changes to all the runs triggered in the batch using the `runs.subscribeToBatch` function.
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
for await (const run of runs.subscribeToBatch("batch-id")) {
|
||||
// This will log the run every time it changes, for all runs in the batch with the ID "batch-id"
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
## Run metadata
|
||||
|
||||
The run metadata API gives you the ability to add or update custom metadata on a run, which will cause the run to be updated. This allows you to extend the Realtime API with custom data attached to a run that can be used for various purposes. Some common use cases include:
|
||||
|
||||
- Adding a link to a related resource
|
||||
- Adding a reference to a user or organization
|
||||
- Adding a custom status with progress information
|
||||
|
||||
See our [run metadata docs](/runs/metadata) for more on how to write tasks that use the metadata API.
|
||||
|
||||
### Using metadata with Realtime & React hooks
|
||||
|
||||
You can combine run metadata with the Realtime API to bridge the gap between your trigger.dev tasks and your applications in two ways:
|
||||
|
||||
1. Using our [React hooks](/realtime/react-hooks/subscribe#using-metadata) to subscribe to metadata updates and update your UI in real-time.
|
||||
2. Using our [backend functions](/realtime/backend) to subscribe to metadata updates in your backend.
|
||||
|
||||
## Limits
|
||||
|
||||
The Realtime API in the Trigger.dev Cloud limits the number of concurrent subscriptions, depending on your plan. If you exceed the limit, you will receive an error when trying to subscribe to a run. For more information, see our [pricing page](https://trigger.dev/pricing).
|
||||
|
||||
## Learn more
|
||||
|
||||
- Read our Realtime blog post ["How we built a real-time service that handles 20,000 updates per second](https://trigger.dev/blog/how-we-built-realtime)
|
||||
- Using Realtime: [React Hooks (frontend)](/realtime/react-hooks)
|
||||
- Using [Backend (server-side)](/realtime/backend)
|
||||
+21
-254
@@ -1,272 +1,39 @@
|
||||
---
|
||||
title: Realtime overview
|
||||
sidebarTitle: Overview
|
||||
description: Using the Trigger.dev v3 realtime API
|
||||
description: Using the Trigger.dev Realtime API to trigger and/or subscribe to runs in real-time.
|
||||
---
|
||||
|
||||
import RealtimeExamplesCards from "/snippets/realtime-examples-cards.mdx";
|
||||
Trigger.dev Realtime allows you to trigger, subscribe to, and get real-time updates for runs. This is useful for monitoring runs, updating UIs, and building real-time dashboards.
|
||||
|
||||
Trigger.dev Realtime is a set of APIs that allow you to subscribe to runs and get real-time updates on the run status. This is useful for monitoring runs, updating UIs, and building realtime dashboards.
|
||||
You can subscribe to real-time updates for different scopes of runs:
|
||||
|
||||
## How it works
|
||||
- **Specific runs** - Monitor individual run progress by run ID
|
||||
- **Runs with specific tags** - Track all runs that have certain [tags](/tags) (e.g., all runs tagged with `user:123`)
|
||||
- **Batch runs** - All runs within a specific batch
|
||||
- **Trigger + subscribe combos** - Trigger a task and immediately subscribe to its run (frontend only)
|
||||
|
||||
The Realtime API is built on top of [Electric SQL](https://electric-sql.com/), an open-source PostgreSQL syncing engine. The Trigger.dev API wraps Electric SQL and provides a simple API to subscribe to [runs](/runs) and get real-time updates.
|
||||
Once subscribed, you'll receive the complete [run object](/realtime/run-object) with automatic real-time updates whenever the [run changes](/realtime/how-it-works#run-changes), including:
|
||||
|
||||
## Walkthrough
|
||||
- **Status changes** - queued → executing → completed, etc.
|
||||
- **Metadata updates** - Custom progress tracking, status updates, user data, etc. ([React hooks](/realtime/react-hooks/subscribe#using-metadata-to-show-progress-in-your-ui) | [backend](/realtime/backend/subscribe#subscribe-to-metadata-updates-from-your-tasks))
|
||||
- **Tag changes** - When [tags](/tags) are added or removed from the run
|
||||
- **Stream data** - Real-time data chunks from your tasks, perfect for AI/LLM streaming ([React hooks](/realtime/react-hooks/streams) | [backend](/realtime/backend/streams))
|
||||
|
||||
<iframe
|
||||
width="100%"
|
||||
height="315"
|
||||
src="https://www.youtube.com/embed/RhJAbSGkS88?si=4Z72SfygeklNI3As"
|
||||
title="YouTube video player"
|
||||
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
referrerPolicy="strict-origin-when-cross-origin"
|
||||
allowFullScreen
|
||||
/>
|
||||
## Using Realtime in your applications
|
||||
|
||||
## Usage
|
||||
### Authentication
|
||||
|
||||
After you trigger a task, you can subscribe to the run using the `runs.subscribeToRun` function. This function returns an async iterator that you can use to get updates on the run status.
|
||||
All Realtime subscriptions require authentication so you can securely trigger and subscribe to runs. See our [authentication guide](/realtime/auth) for more details.
|
||||
|
||||
```ts
|
||||
import { runs, tasks } from "@trigger.dev/sdk/v3";
|
||||
### Frontend implementation
|
||||
|
||||
// Somewhere in your backend code
|
||||
async function myBackend() {
|
||||
const handle = await tasks.trigger("my-task", { some: "data" });
|
||||
Use our React hooks to build real-time UIs that update as runs execute. Ideal for progress bars, status indicators, and live dashboards.
|
||||
|
||||
for await (const run of runs.subscribeToRun(handle.id)) {
|
||||
// This will log the run every time it changes
|
||||
console.log(run);
|
||||
}
|
||||
}
|
||||
```
|
||||
→ **[See our React hooks guide](/realtime/react-hooks/)**
|
||||
|
||||
Every time the run changes, the async iterator will yield the updated run. You can use this to update your UI, log the run status, or take any other action.
|
||||
### Backend implementation
|
||||
|
||||
Alternatively, you can subscribe to changes to any run that includes a specific tag (or tags) using the `runs.subscribeToRunsWithTag` function.
|
||||
Use our server-side SDK to subscribe to runs from your backend code, other tasks, or serverless functions. Perfect for triggering workflows, sending notifications, or updating databases based on run status changes.
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
for await (const run of runs.subscribeToRunsWithTag("user:1234")) {
|
||||
// This will log the run every time it changes, for all runs with the tag "user:1234"
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
If you've used `batchTrigger` to trigger multiple runs, you can also subscribe to changes to all the runs triggered in the batch using the `runs.subscribeToBatch` function.
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
for await (const run of runs.subscribeToBatch("batch-id")) {
|
||||
// This will log the run every time it changes, for all runs in the batch with the ID "batch-id"
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
### React hooks
|
||||
|
||||
We also provide a set of React hooks that make it easy to use the Realtime API in your React components. See the [React hooks doc](/frontend/react-hooks) for more information.
|
||||
|
||||
## Run changes
|
||||
|
||||
You will receive updates whenever a run changes for the following reasons:
|
||||
|
||||
- The run moves to a new state. See our [run lifecycle docs](/runs#the-run-lifecycle) for more information.
|
||||
- [Run tags](/tags) are added or removed.
|
||||
- [Run metadata](/runs/metadata) is updated.
|
||||
|
||||
## Run object
|
||||
|
||||
The run object returned by the async iterator is NOT the same as the run object returned by the `runs.retrieve` function. This is because Electric SQL streams changes from a single PostgreSQL table, and the run object returned by `runs.retrieve` is a combination of multiple tables.
|
||||
|
||||
The run object returned by the async iterator has the following fields:
|
||||
|
||||
<ParamField path="id" type="string" required>
|
||||
The run ID.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="taskIdentifier" type="string" required>
|
||||
The task identifier.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="payload" type="object" required>
|
||||
The input payload for the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="output" type="object">
|
||||
The output result of the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="createdAt" type="Date" required>
|
||||
Timestamp when the run was created.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="updatedAt" type="Date" required>
|
||||
Timestamp when the run was last updated.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="number" type="number" required>
|
||||
Sequential number assigned to the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="status" type="RunStatus" required>
|
||||
Current status of the run.
|
||||
|
||||
<Accordion title="RunStatus enum">
|
||||
|
||||
| Status | Description |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| `WAITING_FOR_DEPLOY` | Task hasn't been deployed yet but is waiting to be executed |
|
||||
| `QUEUED` | Run is waiting to be executed by a worker |
|
||||
| `EXECUTING` | Run is currently being executed by a worker |
|
||||
| `REATTEMPTING` | Run has failed and is waiting to be retried |
|
||||
| `FROZEN` | Run has been paused by the system, and will be resumed by the system |
|
||||
| `COMPLETED` | Run has been completed successfully |
|
||||
| `CANCELED` | Run has been canceled by the user |
|
||||
| `FAILED` | Run has been completed with errors |
|
||||
| `CRASHED` | Run has crashed and won't be retried, most likely the worker ran out of resources, e.g. memory or storage |
|
||||
| `INTERRUPTED` | Run was interrupted during execution, mostly this happens in development environments |
|
||||
| `SYSTEM_FAILURE` | Run has failed to complete, due to an error in the system |
|
||||
| `DELAYED` | Run has been scheduled to run at a specific time |
|
||||
| `EXPIRED` | Run has expired and won't be executed |
|
||||
| `TIMED_OUT` | Run has reached it's maxDuration and has been stopped |
|
||||
|
||||
</Accordion>
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="durationMs" type="number" required>
|
||||
Duration of the run in milliseconds.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="costInCents" type="number" required>
|
||||
Total cost of the run in cents.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="baseCostInCents" type="number" required>
|
||||
Base cost of the run in cents before any additional charges.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="tags" type="string[]" required>
|
||||
Array of tags associated with the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="idempotencyKey" type="string">
|
||||
Key used to ensure idempotent execution.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="expiredAt" type="Date">
|
||||
Timestamp when the run expired.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="ttl" type="string">
|
||||
Time-to-live duration for the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="finishedAt" type="Date">
|
||||
Timestamp when the run finished.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="startedAt" type="Date">
|
||||
Timestamp when the run started.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="delayedUntil" type="Date">
|
||||
Timestamp until which the run is delayed.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="queuedAt" type="Date">
|
||||
Timestamp when the run was queued.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="metadata" type="Record<string, DeserializedJson>">
|
||||
Additional metadata associated with the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="error" type="SerializedError">
|
||||
Error information if the run failed.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="isTest" type="boolean" required>
|
||||
Indicates whether this is a test run.
|
||||
</ParamField>
|
||||
|
||||
## Type-safety
|
||||
|
||||
You can infer the types of the run's payload and output by passing the type of the task to the `subscribeToRun` function. This will give you type-safe access to the run's payload and output.
|
||||
|
||||
```ts
|
||||
import { runs, tasks } from "@trigger.dev/sdk/v3";
|
||||
import type { myTask } from "./trigger/my-task";
|
||||
|
||||
// Somewhere in your backend code
|
||||
async function myBackend() {
|
||||
const handle = await tasks.trigger("my-task", { some: "data" });
|
||||
|
||||
for await (const run of runs.subscribeToRun<typeof myTask>(handle.id)) {
|
||||
// This will log the run every time it changes
|
||||
console.log(run.payload.some);
|
||||
|
||||
if (run.output) {
|
||||
// This will log the output if it exists
|
||||
console.log(run.output.some);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When using `subscribeToRunsWithTag`, you can pass a union of task types for all the possible tasks that can have the tag.
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
import type { myTask, myOtherTask } from "./trigger/my-task";
|
||||
|
||||
// Somewhere in your backend code
|
||||
for await (const run of runs.subscribeToRunsWithTag<typeof myTask | typeof myOtherTask>("my-tag")) {
|
||||
// You can narrow down the type based on the taskIdentifier
|
||||
switch (run.taskIdentifier) {
|
||||
case "my-task": {
|
||||
console.log("Run output:", run.output.foo); // This will be type-safe
|
||||
break;
|
||||
}
|
||||
case "my-other-task": {
|
||||
console.log("Run output:", run.output.bar); // This will be type-safe
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Run metadata
|
||||
|
||||
The run metadata API gives you the ability to add or update custom metadata on a run, which will cause the run to be updated. This allows you to extend the realtime API with custom data attached to a run that can be used for various purposes. Some common use cases include:
|
||||
|
||||
- Adding a link to a related resource
|
||||
- Adding a reference to a user or organization
|
||||
- Adding a custom status with progress information
|
||||
|
||||
See our [run metadata docs](/runs/metadata) for more on how to use this feature.
|
||||
|
||||
### Using w/Realtime & React hooks
|
||||
|
||||
We suggest combining run metadata with the realtime API and our [React hooks](/frontend/react-hooks) to bridge the gap between your trigger.dev tasks and your UI. This allows you to update your UI in real-time based on changes to the run metadata. As a simple example, you could add a custom status to a run with a progress value, and update your UI based on that progress.
|
||||
|
||||
We have a full demo app repo available [here](https://github.com/triggerdotdev/nextjs-realtime-simple-demo)
|
||||
|
||||
## Realtime streams
|
||||
|
||||
See our dedicated [Realtime streams](/realtime/streams) documentation for more information on how to use the Realtime streams API.
|
||||
|
||||
## Limits
|
||||
|
||||
The Realtime API in the Trigger.dev Cloud limits the number of concurrent subscriptions, depending on your plan. If you exceed the limit, you will receive an error when trying to subscribe to a run. For more information, see our [pricing page](https://trigger.dev/pricing).
|
||||
|
||||
## Known issues
|
||||
|
||||
There is currently a known issue where the realtime API does not work if subscribing to a run that has a large payload or large output and are stored in object store instead of the database. We are working on a fix for this issue: https://github.com/triggerdotdev/trigger.dev/issues/1451. As a workaround you'll need to keep payloads and outputs below 128KB when using the realtime API.
|
||||
|
||||
<RealtimeExamplesCards />
|
||||
→ **[See our Backend guide](/realtime/backend)**
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
title: Realtime React hooks
|
||||
sidebarTitle: React hooks
|
||||
description: Subscribes to all changes to a run in a React component.
|
||||
---
|
||||
|
||||
See our [React hooks](/frontend/react-hooks) for more information about how to use the Realtime API from your frontend application.
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: React hooks overview
|
||||
sidebarTitle: Overview
|
||||
description: Using the Trigger.dev Realtime API from your React applications.
|
||||
---
|
||||
|
||||
import RealtimeExamplesCards from "/snippets/realtime-examples-cards.mdx";
|
||||
|
||||
Our React hooks package provides a set of hooks that make it easy to interact with the Trigger.dev Realtime API from your React applications. You can use these hooks to subscribe to real-time updates, and trigger tasks from your frontend.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the `@trigger.dev/react-hooks` package in your project:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn install @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Authentication
|
||||
|
||||
All hooks require authentication with a Public Access Token. Pass the token via the `accessToken` option:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: "https://your-trigger-dev-instance.com", // optional, only needed if you are self-hosting Trigger.dev
|
||||
});
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Learn more about [generating and managing tokens in our authentication guide](/realtime/auth).
|
||||
|
||||
## Available hooks
|
||||
|
||||
We provide several categories of hooks:
|
||||
|
||||
- **[Triggering hooks](/realtime/react-hooks/triggering)** - Trigger tasks from your frontend application
|
||||
- **[Subscribe hooks](/realtime/react-hooks/subscribe)** - Subscribe to runs, batches, metadata, and more
|
||||
- **[Streams hooks](/realtime/react-hooks/streams)** - Subscribe to real-time streams from your tasks
|
||||
- **[SWR hooks](/realtime/react-hooks/swr)** - Fetch data once and cache it using SWR
|
||||
|
||||
## SWR vs Realtime hooks
|
||||
|
||||
We offer two "styles" of hooks: SWR and Realtime. The SWR hooks use the [swr](https://swr.vercel.app/) library to fetch data once and cache it. The Realtime hooks use [Trigger.dev Realtime](/realtime) to subscribe to updates in real-time.
|
||||
|
||||
<Note>
|
||||
It can be a little confusing which one to use because [swr](https://swr.vercel.app/) can also be
|
||||
configured to poll for updates. But because of rate-limits and the way the Trigger.dev API works,
|
||||
we recommend using the Realtime hooks for most use-cases.
|
||||
</Note>
|
||||
@@ -0,0 +1,221 @@
|
||||
---
|
||||
title: Streams hooks
|
||||
sidebarTitle: Streams
|
||||
description: Subscribe to real-time streams from your tasks in React components
|
||||
---
|
||||
|
||||
These hooks allow you to consume real-time streams from your tasks. Streams are useful for displaying AI/LLM outputs as they're generated, or any other real-time data from your tasks.
|
||||
|
||||
<Note>
|
||||
To learn how to emit streams from your tasks, see our [backend streams
|
||||
documentation](/realtime/backend/streams).
|
||||
</Note>
|
||||
|
||||
## useRealtimeRunWithStreams
|
||||
|
||||
The `useRealtimeRunWithStreams` hook allows you to subscribe to a run by its ID and also receive any streams that are emitted by the task.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, streams, error } = useRealtimeRunWithStreams(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>
|
||||
{Object.keys(streams).map((stream) => (
|
||||
<div key={stream}>Stream: {stream}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
You can also provide the type of the streams to the `useRealtimeRunWithStreams` hook to get type-safety:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
type STREAMS = {
|
||||
openai: string; // this is the type of each "part" of the stream
|
||||
};
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, streams, error } = useRealtimeRunWithStreams<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const text = streams.openai?.map((part) => part).join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
As you can see above, each stream is an array of the type you provided, keyed by the stream name. If instead of a pure text stream you have a stream of objects, you can provide the type of the object:
|
||||
|
||||
```tsx
|
||||
import type { TextStreamPart } from "ai";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
type STREAMS = { openai: TextStreamPart<{}> };
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, streams, error } = useRealtimeRunWithStreams<typeof myTask, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
const text = streams.openai
|
||||
?.filter((stream) => stream.type === "text-delta")
|
||||
?.map((part) => part.text)
|
||||
.join("");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Streaming AI responses
|
||||
|
||||
Here's a more complete example showing how to display streaming OpenAI responses:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
import type { aiStreaming, STREAMS } from "./trigger/ai-streaming";
|
||||
|
||||
function MyComponent({ runId, publicAccessToken }: { runId: string; publicAccessToken: string }) {
|
||||
const { streams } = useRealtimeRunWithStreams<typeof aiStreaming, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (!streams.openai) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
const text = streams.openai.join(""); // `streams.openai` is an array of strings
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>OpenAI response:</h2>
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## AI SDK with tools
|
||||
|
||||
When using the AI SDK with tools, you can access tool calls and results:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
import type { aiStreamingWithTools, STREAMS } from "./trigger/ai-streaming";
|
||||
|
||||
function MyComponent({ runId, publicAccessToken }: { runId: string; publicAccessToken: string }) {
|
||||
const { streams } = useRealtimeRunWithStreams<typeof aiStreamingWithTools, STREAMS>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (!streams.openai) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
// streams.openai is an array of TextStreamPart
|
||||
const toolCall = streams.openai.find(
|
||||
(stream) => stream.type === "tool-call" && stream.toolName === "getWeather"
|
||||
);
|
||||
const toolResult = streams.openai.find((stream) => stream.type === "tool-result");
|
||||
const textDeltas = streams.openai.filter((stream) => stream.type === "text-delta");
|
||||
|
||||
const text = textDeltas.map((delta) => delta.textDelta).join("");
|
||||
const weatherLocation = toolCall ? toolCall.args.location : undefined;
|
||||
const weather = toolResult ? toolResult.result.temperature : undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>OpenAI response:</h2>
|
||||
<p>{text}</p>
|
||||
<h2>Weather:</h2>
|
||||
<p>
|
||||
{weatherLocation
|
||||
? `The weather in ${weatherLocation} is ${weather} degrees.`
|
||||
: "No weather data"}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Common options
|
||||
|
||||
### experimental_throttleInMs
|
||||
|
||||
The `*withStreams` variants of the Realtime hooks accept an `experimental_throttleInMs` option to throttle the updates from the server. This can be useful if you are getting too many updates and want to reduce the number of updates.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunWithStreams } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, streams, error } = useRealtimeRunWithStreams(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
experimental_throttleInMs: 1000, // Throttle updates to once per second
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Run: {run.id}</div>
|
||||
{/* Display streams */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
All other options (accessToken, baseURL, enabled, id) work the same as the other realtime hooks.
|
||||
@@ -0,0 +1,630 @@
|
||||
---
|
||||
title: Subscribing to runs
|
||||
sidebarTitle: Subscribe
|
||||
description: Get live updates from runs, batches, metadata, and more in your frontend application.
|
||||
---
|
||||
|
||||
These hooks allow you to subscribe to runs, batches, and streams using [Trigger.dev Realtime](/realtime). They automatically include real-time updates for run status, metadata, output, and other properties.
|
||||
|
||||
## Hooks
|
||||
|
||||
## Triggering + Realtime hooks
|
||||
|
||||
For triggering tasks and immediately subscribing to their runs, we provide combo hooks, details on how to use them can be found in the [triggering](/realtime/react-hooks/triggering) section.
|
||||
|
||||
- **[`useRealtimeTaskTrigger`](/realtime/react-hooks/triggering#userealtimetasktrigger)** - Trigger a task and subscribe to the run
|
||||
- **[`useRealtimeTaskTriggerWithStreams`](/realtime/react-hooks/triggering#userealtimetasktriggerwithstreams)** - Trigger a task and subscribe to both run updates and streams
|
||||
|
||||
## Other Realtime subscription hooks
|
||||
|
||||
### useRealtimeRun
|
||||
|
||||
The `useRealtimeRun` hook allows you to subscribe to a run by its ID.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
To correctly type the run's payload and output, you can provide the type of your task to the `useRealtimeRun` hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun<typeof myTask>(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
You can supply an `onComplete` callback to the `useRealtimeRun` hook to be called when the run is completed or errored. This is useful if you want to perform some action when the run is completed, like navigating to a different page or showing a notification.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
onComplete: (run, error) => {
|
||||
console.log("Run completed", run);
|
||||
},
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
See our [run object reference](/realtime/run-object) for the complete schema and [How it Works documentation](/realtime/how-it-works) for more technical details.
|
||||
|
||||
### useRealtimeRunsWithTag
|
||||
|
||||
The `useRealtimeRunsWithTag` hook allows you to subscribe to multiple runs with a specific tag.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
To correctly type the runs payload and output, you can provide the type of your task to the `useRealtimeRunsWithTag` hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag<typeof myTask>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now runs[i].payload and runs[i].output are correctly typed
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
If `useRealtimeRunsWithTag` could return multiple different types of tasks, you can pass a union of all the task types to the hook:
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRunsWithTag } from "@trigger.dev/react-hooks";
|
||||
import type { myTask1, myTask2 } from "@/trigger/myTasks";
|
||||
|
||||
export function MyComponent({ tag }: { tag: string }) {
|
||||
const { runs, error } = useRealtimeRunsWithTag<typeof myTask1 | typeof myTask2>(tag);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// You can narrow down the type of the run based on the taskIdentifier
|
||||
for (const run of runs) {
|
||||
if (run.taskIdentifier === "my-task-1") {
|
||||
// run is correctly typed as myTask1
|
||||
} else if (run.taskIdentifier === "my-task-2") {
|
||||
// run is correctly typed as myTask2
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### useRealtimeBatch
|
||||
|
||||
The `useRealtimeBatch` hook allows you to subscribe to a batch of runs by its the batch ID.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeBatch } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({ batchId }: { batchId: string }) {
|
||||
const { runs, error } = useRealtimeBatch(batchId);
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{runs.map((run) => (
|
||||
<div key={run.id}>Run: {run.id}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
See our [Realtime documentation](/realtime) for more information.
|
||||
|
||||
## Using metadata to show progress in your UI
|
||||
|
||||
All realtime hooks automatically include metadata updates. Whenever your task updates metadata using `metadata.set()`, `metadata.append()`, or other metadata methods, your component will re-render with the updated data.
|
||||
|
||||
<Note>To learn how to write tasks using metadata, see our [metadata](/runs/metadata) guide.</Note>
|
||||
|
||||
### Progress monitoring
|
||||
|
||||
This example demonstrates how to create a progress monitor component that can be used to display the progress of a run:
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function ProgressMonitor({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error, isLoading } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
if (isLoading) return <div>Loading run...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
if (!run) return <div>Run not found</div>;
|
||||
|
||||
const progress = run.metadata?.progress as
|
||||
| {
|
||||
current: number;
|
||||
total: number;
|
||||
percentage: number;
|
||||
currentItem: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h3>Run Status: {run.status}</h3>
|
||||
<p>Run ID: {run.id}</p>
|
||||
</div>
|
||||
|
||||
{progress && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>Progress</span>
|
||||
<span>{progress.percentage}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
|
||||
style={{ width: `${progress.percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600">
|
||||
Processing: {progress.currentItem} ({progress.current}/{progress.total})
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Reusable progress bar
|
||||
|
||||
This example demonstrates how to create a reusable progress bar component that can be used to display the percentage progress of a run:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
interface ProgressBarProps {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export function ProgressBar({ runId, publicAccessToken, title }: ProgressBarProps) {
|
||||
const { run } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
const progress = run?.metadata?.progress as
|
||||
| {
|
||||
current?: number;
|
||||
total?: number;
|
||||
percentage?: number;
|
||||
currentItem?: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
const percentage = progress?.percentage ?? 0;
|
||||
const isComplete = run?.status === "COMPLETED";
|
||||
const isFailed = run?.status === "FAILED";
|
||||
|
||||
return (
|
||||
<div className="w-full space-y-2">
|
||||
{title && <h4 className="font-medium">{title}</h4>}
|
||||
|
||||
<div className="w-full bg-gray-200 rounded-full h-3">
|
||||
<div
|
||||
className={`h-3 rounded-full transition-all duration-500 ${
|
||||
isFailed ? "bg-red-500" : isComplete ? "bg-green-500" : "bg-blue-500"
|
||||
}`}
|
||||
style={{ width: `${percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between text-sm text-gray-600">
|
||||
<span>
|
||||
{progress?.current && progress?.total
|
||||
? `${progress.current}/${progress.total} items`
|
||||
: "Processing..."}
|
||||
</span>
|
||||
<span>{percentage}%</span>
|
||||
</div>
|
||||
|
||||
{progress?.currentItem && (
|
||||
<p className="text-sm text-gray-500 truncate">Current: {progress.currentItem}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Status indicator with logs
|
||||
|
||||
This example demonstrates how to create a status indicator component that can be used to display the status of a run, and also logs that are emitted by the task:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
interface StatusIndicatorProps {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}
|
||||
|
||||
export function StatusIndicator({ runId, publicAccessToken }: StatusIndicatorProps) {
|
||||
const { run } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
const status = run?.metadata?.status as string | undefined;
|
||||
const logs = run?.metadata?.logs as string[] | undefined;
|
||||
|
||||
const getStatusColor = (status: string | undefined) => {
|
||||
switch (status) {
|
||||
case "completed":
|
||||
return "text-green-600 bg-green-100";
|
||||
case "failed":
|
||||
return "text-red-600 bg-red-100";
|
||||
case "running":
|
||||
return "text-blue-600 bg-blue-100";
|
||||
default:
|
||||
return "text-gray-600 bg-gray-100";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className={`px-3 py-1 rounded-full text-sm font-medium ${getStatusColor(status)}`}>
|
||||
{status || run?.status || "Unknown"}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">Run {run?.id}</span>
|
||||
</div>
|
||||
|
||||
{logs && logs.length > 0 && (
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<h4 className="font-medium mb-2">Logs</h4>
|
||||
<div className="space-y-1 max-h-48 overflow-y-auto">
|
||||
{logs.map((log, index) => (
|
||||
<div key={index} className="text-sm text-gray-700 font-mono">
|
||||
{log}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-stage deployment monitor
|
||||
|
||||
This example demonstrates how to create a multi-stage deployment monitor component that can be used to display the progress of a deployment:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
interface DeploymentMonitorProps {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}
|
||||
|
||||
const DEPLOYMENT_STAGES = [
|
||||
"initializing",
|
||||
"building",
|
||||
"testing",
|
||||
"deploying",
|
||||
"verifying",
|
||||
"completed",
|
||||
] as const;
|
||||
|
||||
export function DeploymentMonitor({ runId, publicAccessToken }: DeploymentMonitorProps) {
|
||||
const { run } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
const status = run?.metadata?.status as string | undefined;
|
||||
const logs = run?.metadata?.logs as string[] | undefined;
|
||||
const currentStageIndex = DEPLOYMENT_STAGES.indexOf(status as any);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-semibold">Deployment Progress</h3>
|
||||
|
||||
{/* Stage indicators */}
|
||||
<div className="space-y-4">
|
||||
{DEPLOYMENT_STAGES.map((stage, index) => {
|
||||
const isActive = currentStageIndex === index;
|
||||
const isCompleted = currentStageIndex > index;
|
||||
const isFailed = run?.status === "FAILED" && currentStageIndex === index;
|
||||
|
||||
return (
|
||||
<div key={stage} className="flex items-center space-x-3">
|
||||
<div
|
||||
className={`w-6 h-6 rounded-full flex items-center justify-center text-sm font-medium ${
|
||||
isFailed
|
||||
? "bg-red-500 text-white"
|
||||
: isCompleted
|
||||
? "bg-green-500 text-white"
|
||||
: isActive
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-200 text-gray-600"
|
||||
}`}
|
||||
>
|
||||
{isCompleted ? "✓" : index + 1}
|
||||
</div>
|
||||
<span
|
||||
className={`capitalize ${
|
||||
isActive
|
||||
? "font-medium text-blue-600"
|
||||
: isCompleted
|
||||
? "text-green-600"
|
||||
: isFailed
|
||||
? "text-red-600"
|
||||
: "text-gray-500"
|
||||
}`}
|
||||
>
|
||||
{stage}
|
||||
</span>
|
||||
{isActive && (
|
||||
<div className="animate-spin w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Recent logs */}
|
||||
{logs && logs.length > 0 && (
|
||||
<div className="bg-black text-green-400 rounded-lg p-4 font-mono text-sm">
|
||||
<div className="space-y-1 max-h-32 overflow-y-auto">
|
||||
{logs.slice(-5).map((log, index) => (
|
||||
<div key={index}>
|
||||
<span className="text-gray-500">$ </span>
|
||||
{log}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Type safety
|
||||
|
||||
Define TypeScript interfaces for your metadata to get full type safety:
|
||||
|
||||
```tsx
|
||||
"use client";
|
||||
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
interface TaskMetadata {
|
||||
progress?: {
|
||||
current: number;
|
||||
total: number;
|
||||
percentage: number;
|
||||
currentItem: string;
|
||||
};
|
||||
status?: "initializing" | "processing" | "completed" | "failed";
|
||||
user?: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
logs?: string[];
|
||||
}
|
||||
|
||||
export function TypedMetadataComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
});
|
||||
|
||||
// Type-safe metadata access
|
||||
const metadata = run?.metadata as TaskMetadata | undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{metadata?.progress && <p>Progress: {metadata.progress.percentage}%</p>}
|
||||
|
||||
{metadata?.user && (
|
||||
<p>
|
||||
User: {metadata.user.name} ({metadata.user.id})
|
||||
</p>
|
||||
)}
|
||||
|
||||
{metadata?.status && <p>Status: {metadata.status}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Common options
|
||||
|
||||
### accessToken & baseURL
|
||||
|
||||
You can pass the `accessToken` option to the Realtime hooks to authenticate the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
baseURL: "https://my-self-hosted-trigger.com", // Optional if you are using a self-hosted Trigger.dev instance
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### enabled
|
||||
|
||||
You can pass the `enabled` option to the Realtime hooks to enable or disable the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
runId,
|
||||
publicAccessToken,
|
||||
enabled,
|
||||
}: {
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
enabled: boolean;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
enabled,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to conditionally disable using the hook based on some state.
|
||||
|
||||
### id
|
||||
|
||||
You can pass the `id` option to the Realtime hooks to change the ID of the subscription.
|
||||
|
||||
```tsx
|
||||
import { useRealtimeRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({
|
||||
id,
|
||||
runId,
|
||||
publicAccessToken,
|
||||
enabled,
|
||||
}: {
|
||||
id: string;
|
||||
runId: string;
|
||||
publicAccessToken: string;
|
||||
enabled: boolean;
|
||||
}) {
|
||||
const { run, error } = useRealtimeRun(runId, {
|
||||
accessToken: publicAccessToken,
|
||||
enabled,
|
||||
id,
|
||||
});
|
||||
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
This allows you to change the ID of the subscription based on some state. Passing in a different ID will unsubscribe from the current subscription and subscribe to the new one (and remove any cached data).
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: SWR hooks
|
||||
sidebarTitle: SWR
|
||||
description: Fetch and cache data using SWR-based hooks
|
||||
---
|
||||
|
||||
SWR hooks use the [swr](https://swr.vercel.app/) library to fetch data once and cache it. These hooks are useful when you need to fetch data without real-time updates.
|
||||
|
||||
<Note>
|
||||
While SWR can be configured to poll for updates, we recommend using our other [Realtime
|
||||
hooks](/realtime/react-hooks/) for most use-cases due to rate-limits and the way the Trigger.dev
|
||||
API works.
|
||||
</Note>
|
||||
|
||||
## useRun
|
||||
|
||||
The `useRun` hook allows you to fetch a run by its ID.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
|
||||
import { useRun } from "@trigger.dev/react-hooks";
|
||||
|
||||
export function MyComponent({ runId }: { runId: string }) {
|
||||
const { run, error, isLoading } = useRun(runId);
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
The `run` object returned is the same as the [run object](/management/runs/retrieve) returned by the Trigger.dev API. To correctly type the run's payload and output, you can provide the type of your task to the `useRun` hook:
|
||||
|
||||
```tsx
|
||||
import { useRun } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
|
||||
export function MyComponent({ runId }: { runId: string }) {
|
||||
const { run, error, isLoading } = useRun<typeof myTask>(runId, {
|
||||
refreshInterval: 0, // Disable polling
|
||||
});
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
// Now run.payload and run.output are correctly typed
|
||||
|
||||
return <div>Run: {run.id}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
## Common SWR options
|
||||
|
||||
You can pass the following options to the all SWR hooks:
|
||||
|
||||
<ParamField path="revalidateOnFocus" type="boolean">
|
||||
Revalidate the data when the window regains focus.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="revalidateOnReconnect" type="boolean">
|
||||
Revalidate the data when the browser regains a network connection.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="refreshInterval" type="number">
|
||||
Poll for updates at the specified interval (in milliseconds). Polling is not recommended for most
|
||||
use-cases. Use the Realtime hooks instead.
|
||||
</ParamField>
|
||||
|
||||
## Common SWR return values
|
||||
|
||||
<ResponseField name="error" type="Error">
|
||||
An error object if an error occurred while fetching the data.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isLoading" type="boolean">
|
||||
A boolean indicating if the data is currently being fetched.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isValidating" type="boolean">
|
||||
A boolean indicating if the data is currently being revalidated.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="isError" type="boolean">
|
||||
A boolean indicating if an error occurred while fetching the data.
|
||||
</ResponseField>{" "}
|
||||
+13
-73
@@ -4,81 +4,23 @@ sidebarTitle: Triggering
|
||||
description: Triggering tasks from your frontend application.
|
||||
---
|
||||
|
||||
import RealtimeExamplesCards from "/snippets/realtime-examples-cards.mdx";
|
||||
|
||||
We provide a set of hooks that can be used to trigger tasks from your frontend application.
|
||||
|
||||
## Demo
|
||||
|
||||
We've created a [Demo application](https://github.com/triggerdotdev/realtime-llm-battle) that demonstrates how to use our React hooks to trigger tasks in a Next.js application. The application uses the `@trigger.dev/react-hooks` package to trigger a task and subscribe to the run in real-time.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the `@trigger.dev/react-hooks` package in your project:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm add @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn install @trigger.dev/react-hooks
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Authentication
|
||||
|
||||
To authenticate a trigger hook, you must provide a special one-time use "trigger" token. These tokens are very similar to [Public Access Tokens](/frontend/overview#authentication), but they can only be used once to trigger a task. You can generate a trigger token using the `auth.createTriggerPublicToken` function in your backend code:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
// Somewhere in your backend code
|
||||
const triggerToken = await auth.createTriggerPublicToken("my-task");
|
||||
```
|
||||
|
||||
These tokens also expire, with the default expiration time being 15 minutes. You can specify a custom expiration time by passing a `expirationTime` parameter:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
// Somewhere in your backend code
|
||||
const triggerToken = await auth.createTriggerPublicToken("my-task", {
|
||||
expirationTime: "24hr",
|
||||
});
|
||||
```
|
||||
|
||||
You can also pass multiple tasks to the `createTriggerPublicToken` function to create a token that can trigger multiple tasks:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
// Somewhere in your backend code
|
||||
const triggerToken = await auth.createTriggerPublicToken(["my-task-1", "my-task-2"]);
|
||||
```
|
||||
|
||||
You can also pass the `multipleUse` parameter to create a token that can be used multiple times:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
const triggerToken = await auth.createTriggerPublicToken("my-task", {
|
||||
multipleUse: true, // ❌ Use this with caution!
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
After generating the trigger token in your backend, you must pass it to your frontend application.
|
||||
We have a guide on how to do this in the [React hooks
|
||||
overview](/frontend/react-hooks/overview#passing-the-token-to-the-frontend).
|
||||
For triggering tasks from your frontend, you need to use “trigger” tokens. These can only be used
|
||||
once to trigger a task and are more secure than regular Public Access Tokens. To learn more about
|
||||
how to create and use these tokens, see our [Trigger
|
||||
Tokens](/realtime/auth#trigger-tokens-for-frontend-triggering-only) documentation.
|
||||
</Note>
|
||||
|
||||
## Hooks
|
||||
|
||||
We provide three hooks for triggering tasks from your frontend application:
|
||||
|
||||
- `useTaskTrigger` - Trigger a task from your frontend application.
|
||||
- `useRealtimeTaskTrigger` - Trigger a task from your frontend application and subscribe to the run.
|
||||
- `useRealtimeTaskTriggerWithStreams` - Trigger a task from your frontend application and subscribe to the run, and also receive any streams that are emitted by the task.
|
||||
|
||||
### useTaskTrigger
|
||||
|
||||
The `useTaskTrigger` hook allows you to trigger a task from your frontend application.
|
||||
@@ -88,7 +30,7 @@ The `useTaskTrigger` hook allows you to trigger a task from your frontend applic
|
||||
|
||||
import { useTaskTrigger } from "@trigger.dev/react-hooks";
|
||||
import type { myTask } from "@/trigger/myTask";
|
||||
// 👆 This is the type of your task
|
||||
// 👆 This is the type of your task, include this to get type-safety
|
||||
|
||||
export function MyComponent({ publicAccessToken }: { publicAccessToken: string }) {
|
||||
// pass the type of your task here 👇
|
||||
@@ -127,7 +69,7 @@ submit({ foo: "bar" }, { tags: ["tag1", "tag2"] });
|
||||
|
||||
#### Using the handle object
|
||||
|
||||
You can use the `handle` object to initiate a subsequent [realtime hook](/frontend/react-hooks/realtime#userealtimerun) to subscribe to the run.
|
||||
You can use the `handle` object to initiate a subsequent [Realtime hook](/realtime/react-hooks/subscribe#userealtimerun) to subscribe to the run.
|
||||
|
||||
```tsx
|
||||
"use client"; // This is needed for Next.js App Router or other RSC frameworks
|
||||
@@ -193,7 +135,7 @@ export function MyComponent({ publicAccessToken }: { publicAccessToken: string }
|
||||
return <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
// This is the realtime run object, which will automatically update when the run changes
|
||||
// This is the Realtime run object, which will automatically update when the run changes
|
||||
if (run) {
|
||||
return <div>Run ID: {run.id}</div>;
|
||||
}
|
||||
@@ -250,5 +192,3 @@ export function MyComponent({ publicAccessToken }: { publicAccessToken: string }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
<RealtimeExamplesCards />
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: "The run object"
|
||||
sidebarTitle: "The run object"
|
||||
description: "The run object schema for Realtime subscriptions"
|
||||
---
|
||||
|
||||
The [run object](/realtime/run-object#the-run-object) is the main object returned by Realtime subscriptions (e.g., `runs.subscribeToRun()`). It contains all the information about the run, including the run ID, task identifier, payload, output, and more.
|
||||
|
||||
Type-safety is supported for the run object, so you can infer the types of the run's payload and output. See [type-safety](#type-safety) for more information.
|
||||
|
||||
## The run object
|
||||
|
||||
### Properties
|
||||
|
||||
<ParamField path="id" type="string" required>
|
||||
The run ID.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="taskIdentifier" type="string" required>
|
||||
The task identifier.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="payload" type="object" required>
|
||||
The input payload for the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="output" type="object">
|
||||
The output result of the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="createdAt" type="Date" required>
|
||||
Timestamp when the run was created.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="updatedAt" type="Date" required>
|
||||
Timestamp when the run was last updated.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="number" type="number" required>
|
||||
Sequential number assigned to the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="status" type="RunStatus" required>
|
||||
Current status of the run.
|
||||
|
||||
<Accordion title="RunStatus enum">
|
||||
|
||||
| Status | Description |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| `WAITING_FOR_DEPLOY` | Task hasn't been deployed yet but is waiting to be executed |
|
||||
| `QUEUED` | Run is waiting to be executed by a worker |
|
||||
| `EXECUTING` | Run is currently being executed by a worker |
|
||||
| `REATTEMPTING` | Run has failed and is waiting to be retried |
|
||||
| `FROZEN` | Run has been paused by the system, and will be resumed by the system |
|
||||
| `COMPLETED` | Run has been completed successfully |
|
||||
| `CANCELED` | Run has been canceled by the user |
|
||||
| `FAILED` | Run has been completed with errors |
|
||||
| `CRASHED` | Run has crashed and won't be retried, most likely the worker ran out of resources, e.g. memory or storage |
|
||||
| `INTERRUPTED` | Run was interrupted during execution, mostly this happens in development environments |
|
||||
| `SYSTEM_FAILURE` | Run has failed to complete, due to an error in the system |
|
||||
| `DELAYED` | Run has been scheduled to run at a specific time |
|
||||
| `EXPIRED` | Run has expired and won't be executed |
|
||||
| `TIMED_OUT` | Run has reached it's maxDuration and has been stopped |
|
||||
|
||||
</Accordion>
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="durationMs" type="number" required>
|
||||
Duration of the run in milliseconds.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="costInCents" type="number" required>
|
||||
Total cost of the run in cents.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="baseCostInCents" type="number" required>
|
||||
Base cost of the run in cents before any additional charges.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="tags" type="string[]" required>
|
||||
Array of tags associated with the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="idempotencyKey" type="string">
|
||||
Key used to ensure idempotent execution.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="expiredAt" type="Date">
|
||||
Timestamp when the run expired.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="ttl" type="string">
|
||||
Time-to-live duration for the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="finishedAt" type="Date">
|
||||
Timestamp when the run finished.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="startedAt" type="Date">
|
||||
Timestamp when the run started.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="delayedUntil" type="Date">
|
||||
Timestamp until which the run is delayed.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="queuedAt" type="Date">
|
||||
Timestamp when the run was queued.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="metadata" type="Record<string, DeserializedJson>">
|
||||
Additional metadata associated with the run.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="error" type="SerializedError">
|
||||
Error information if the run failed.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="isTest" type="boolean" required>
|
||||
Indicates whether this is a test run.
|
||||
</ParamField>
|
||||
|
||||
## Type-safety
|
||||
|
||||
You can infer the types of the run's payload and output by passing the type of the task to the `subscribeToRun` function. This will give you type-safe access to the run's payload and output.
|
||||
|
||||
```ts
|
||||
import { runs, tasks } from "@trigger.dev/sdk/v3";
|
||||
import type { myTask } from "./trigger/my-task";
|
||||
|
||||
// Somewhere in your backend code
|
||||
async function myBackend() {
|
||||
const handle = await tasks.trigger("my-task", { some: "data" });
|
||||
|
||||
for await (const run of runs.subscribeToRun<typeof myTask>(handle.id)) {
|
||||
// This will log the run every time it changes
|
||||
console.log(run.payload.some);
|
||||
|
||||
if (run.output) {
|
||||
// This will log the output if it exists
|
||||
console.log(run.output.some);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When using `subscribeToRunsWithTag`, you can pass a union of task types for all the possible tasks that can have the tag.
|
||||
|
||||
```ts
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
import type { myTask, myOtherTask } from "./trigger/my-task";
|
||||
|
||||
// Somewhere in your backend code
|
||||
for await (const run of runs.subscribeToRunsWithTag<typeof myTask | typeof myOtherTask>("my-tag")) {
|
||||
// You can narrow down the type based on the taskIdentifier
|
||||
switch (run.taskIdentifier) {
|
||||
case "my-task": {
|
||||
console.log("Run output:", run.output.foo); // This will be type-safe
|
||||
break;
|
||||
}
|
||||
case "my-other-task": {
|
||||
console.log("Run output:", run.output.bar); // This will be type-safe
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This works with all realtime subscription functions:
|
||||
|
||||
- `runs.subscribeToRun<TaskType>()`
|
||||
- `runs.subscribeToRunsWithTag<TaskType>()`
|
||||
- `runs.subscribeToBatch<TaskType>()`
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: runs.subscribeToBatch
|
||||
sidebarTitle: subscribeToBatch
|
||||
description: Subscribes to all changes for runs in a batch.
|
||||
---
|
||||
|
||||
import RunObject from "/snippets/realtime/run-object.mdx";
|
||||
|
||||
<RequestExample>
|
||||
|
||||
```ts Example
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
for await (const run of runs.subscribeToBatch("batch_1234")) {
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
</RequestExample>
|
||||
|
||||
This function subscribes to all changes for runs in a batch. It returns an async iterator that yields the a run object whenever a run in the batch is updated. The iterator does not complete on it's own, you must manually `break` the loop when you want to stop listening for updates.
|
||||
|
||||
### Authentication
|
||||
|
||||
This function supports both server-side and client-side authentication. For server-side authentication, use your API key. For client-side authentication, you must generate a public access token with one of the following scopes:
|
||||
|
||||
- `read:batch:<batchId>`
|
||||
- `read:runs` will provide access to all runs (not recommended for production use)
|
||||
|
||||
To generate a public access token, use the `auth.createPublicToken` function:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
batch: ["batch_1234"],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
The AsyncIterator yields an object with the following properties:
|
||||
|
||||
<RunObject />
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: runs.subscribeToRun
|
||||
sidebarTitle: subscribeToRun
|
||||
description: Subscribes to all changes to a run.
|
||||
---
|
||||
|
||||
import RunObject from "/snippets/realtime/run-object.mdx";
|
||||
|
||||
<RequestExample>
|
||||
|
||||
```ts Example
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
for await (const run of runs.subscribeToRun("run_1234")) {
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
</RequestExample>
|
||||
|
||||
This function subscribes to all changes to a run. It returns an async iterator that yields the run object whenever the run is updated. The iterator will complete when the run is finished.
|
||||
|
||||
### Authentication
|
||||
|
||||
This function supports both server-side and client-side authentication. For server-side authentication, use your API key. For client-side authentication, you must generate a public access token with one of the following scopes:
|
||||
|
||||
- `read:runs`
|
||||
- `read:runs:<runId>`
|
||||
|
||||
To generate a public access token, use the `auth.createPublicToken` function:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
runs: ["run_1234"],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
The AsyncIterator yields an object with the following properties:
|
||||
|
||||
<RunObject />
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: runs.subscribeToRunsWithTag
|
||||
sidebarTitle: subscribeToRunsWithTag
|
||||
description: Subscribes to all changes to runs with a specific tag.
|
||||
---
|
||||
|
||||
import RunObject from "/snippets/realtime/run-object.mdx";
|
||||
|
||||
<RequestExample>
|
||||
|
||||
```ts Example
|
||||
import { runs } from "@trigger.dev/sdk/v3";
|
||||
|
||||
for await (const run of runs.subscribeToRunsWithTag("user:1234")) {
|
||||
console.log(run);
|
||||
}
|
||||
```
|
||||
|
||||
</RequestExample>
|
||||
|
||||
This function subscribes to all changes to runs with a specific tag. It returns an async iterator that yields the run object whenever a run with the specified tag is updated. This iterator will never complete, so you must manually break out of the loop when you no longer want to receive updates.
|
||||
|
||||
### Authentication
|
||||
|
||||
This function supports both server-side and client-side authentication. For server-side authentication, use your API key. For client-side authentication, you must generate a public access token with one of the following scopes:
|
||||
|
||||
- `read:runs`
|
||||
- `read:tags:<tagName>`
|
||||
|
||||
To generate a public access token, use the `auth.createPublicToken` function:
|
||||
|
||||
```ts
|
||||
import { auth } from "@trigger.dev/sdk/v3";
|
||||
|
||||
// Somewhere in your backend code
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
tags: ["user:1234"],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
The AsyncIterator yields an object with the following properties:
|
||||
|
||||
<RunObject />
|
||||
+99
-1
@@ -264,7 +264,7 @@ export const myTask = task({
|
||||
|
||||
### stream
|
||||
|
||||
Capture a stream of values and make the stream available when using Realtime. See our [Realtime streams](/realtime/streams) documentation for more information.
|
||||
Capture a stream of values and make the stream available when using Realtime. See our [Realtime streams](/realtime/react-hooks/streams) documentation for more information.
|
||||
|
||||
```ts
|
||||
import { task, metadata } from "@trigger.dev/sdk/v3";
|
||||
@@ -514,6 +514,104 @@ Combined with [Realtime](/realtime), you could use this to show a live progress
|
||||
height="100%"
|
||||
/>
|
||||
|
||||
## More metadata task examples
|
||||
|
||||
Using metadata updates in conjunction with our [Realtime React hooks](/realtime/react-hooks/overview) can be a powerful way to build real-time UIs. Here are some example tasks demonstrating how to use metadata in your tasks to track progress, status, and more:
|
||||
|
||||
### Progress tracking
|
||||
|
||||
Track progress with percentage and current step:
|
||||
|
||||
```ts
|
||||
import { task, metadata } from "@trigger.dev/sdk/v3";
|
||||
|
||||
export const batchProcessingTask = task({
|
||||
id: "batch-processing",
|
||||
run: async (payload: { records: any[] }) => {
|
||||
for (let i = 0; i < payload.records.length; i++) {
|
||||
const record = payload.records[i];
|
||||
|
||||
// Update progress
|
||||
metadata.set("progress", {
|
||||
step: i + 1,
|
||||
total: payload.records.length,
|
||||
percentage: Math.round(((i + 1) / payload.records.length) * 100),
|
||||
currentRecord: record.id,
|
||||
});
|
||||
|
||||
await processRecord(record);
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Status updates with logs
|
||||
|
||||
Append log entries while maintaining status:
|
||||
|
||||
```ts
|
||||
import { task, metadata } from "@trigger.dev/sdk/v3";
|
||||
|
||||
export const deploymentTask = task({
|
||||
id: "deployment",
|
||||
run: async (payload: { version: string }) => {
|
||||
metadata.set("status", "initializing");
|
||||
metadata.append("logs", "Starting deployment...");
|
||||
|
||||
// Step 1
|
||||
metadata.set("status", "building");
|
||||
metadata.append("logs", "Building application...");
|
||||
await buildApplication();
|
||||
|
||||
// Step 2
|
||||
metadata.set("status", "deploying");
|
||||
metadata.append("logs", "Deploying to production...");
|
||||
await deployToProduction();
|
||||
|
||||
// Step 3
|
||||
metadata.set("status", "verifying");
|
||||
metadata.append("logs", "Running health checks...");
|
||||
await runHealthChecks();
|
||||
|
||||
metadata.set("status", "completed");
|
||||
metadata.append("logs", "Deployment successful!");
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### User context and notifications
|
||||
|
||||
Store user information and notification preferences:
|
||||
|
||||
```ts
|
||||
import { task, metadata } from "@trigger.dev/sdk/v3";
|
||||
|
||||
export const userTask = task({
|
||||
id: "user-task",
|
||||
run: async (payload: { userId: string; action: string }) => {
|
||||
// Set user context in metadata
|
||||
metadata.set("user", {
|
||||
id: payload.userId,
|
||||
action: payload.action,
|
||||
startedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Update status for user notifications
|
||||
metadata.set("notification", {
|
||||
type: "info",
|
||||
message: `Starting ${payload.action} for user ${payload.userId}`,
|
||||
});
|
||||
|
||||
await performUserAction(payload);
|
||||
|
||||
metadata.set("notification", {
|
||||
type: "success",
|
||||
message: `${payload.action} completed successfully`,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Metadata propagation
|
||||
|
||||
Metadata is NOT propagated to child tasks. If you want to pass metadata to a child task, you must do so explicitly:
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
## Examples
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Claude Thinking Chatbot"
|
||||
icon="messages"
|
||||
href="/guides/example-projects/claude-thinking-chatbot"
|
||||
>
|
||||
A chatbot using the AI SDK that demonstrates Anthropic Claude 3.7's thinking capabilities through the Realtime API and
|
||||
react hooks.
|
||||
</Card>
|
||||
<Card
|
||||
title="Image Generation with fal.ai"
|
||||
icon="image"
|
||||
@@ -25,14 +33,6 @@
|
||||
A Next.js app that uses the Realtime API and react hooks to evaluate language model responses on
|
||||
the frontend in real-time.
|
||||
</Card>
|
||||
<Card
|
||||
title="Claude Thinking Chatbot"
|
||||
icon="messages"
|
||||
href="/guides/example-projects/claude-thinking-chatbot"
|
||||
>
|
||||
A chatbot that demonstrates Claude 3.7's thinking capabilities through the Realtime API and
|
||||
react hooks.
|
||||
</Card>
|
||||
|
||||
</CardGroup>
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
To learn more, take a look at the following resources:
|
||||
|
||||
- [Trigger.dev Realtime](/realtime) - learn more about how to subscribe to runs and get real-time updates
|
||||
- [Realtime streaming](/realtime/streams) - learn more about streaming data from your tasks
|
||||
- [Realtime streaming](/realtime/react-hooks/streams) - learn more about streaming data from your tasks
|
||||
- [Batch Triggering](/triggering#tasks-batchtrigger) - learn more about how to trigger tasks in batches
|
||||
- [React hooks](/frontend/react-hooks) - learn more about using React hooks to interact with the Trigger.dev API
|
||||
- [React hooks](/realtime/react-hooks) - learn more about using React hooks to interact with the Trigger.dev API
|
||||
|
||||
+1
-1
@@ -704,7 +704,7 @@ export const childTask2 = task({
|
||||
## Triggering from your frontend
|
||||
|
||||
If you want to trigger a task directly from a frontend application, you can use our [React
|
||||
hooks](/frontend/react-hooks/triggering).
|
||||
hooks](/realtime/react-hooks/triggering).
|
||||
|
||||
## Options
|
||||
|
||||
|
||||
@@ -3,12 +3,21 @@ title: "Video walkthrough"
|
||||
description: "Go from zero to a working task in your Next.js app in 10 minutes."
|
||||
---
|
||||
|
||||
<iframe width="100%" height="315" src="https://www.youtube.com/embed/YH_4c0K7fGM?si=5JzZmZseuqI5aciM" title="Trigger.dev walkthrough" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen/>
|
||||
{" "}
|
||||
<iframe
|
||||
width="100%"
|
||||
height="315"
|
||||
src="https://www.youtube.com/embed/YH_4c0K7fGM?si=5JzZmZseuqI5aciM"
|
||||
title="Trigger.dev walkthrough"
|
||||
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
referrerPolicy="strict-origin-when-cross-origin"
|
||||
allowFullScreen
|
||||
/>
|
||||
|
||||
### In this video we cover the following topics:
|
||||
|
||||
- [0:00](https://youtu.be/YH_4c0K7fGM?si=J8svVzotZtyTXDap&t=0) – [Install Trigger.dev](/quick-start) in an existing Next.js project
|
||||
- [1:44](https://youtu.be/YH_4c0K7fGM?si=J8svVzotZtyTXDap&t=104) – [Run and test](/run-tests) the "Hello, world!" example project
|
||||
- [2:09](https://youtu.be/YH_4c0K7fGM?si=FMTP8ep_cDBCU0_x&t=128) – Create and run an AI image generation task that uses [Fal.ai](https://fal.ai) – ([View the code](/guides/examples/fal-ai-image-to-cartoon))
|
||||
- [6:25](https://youtu.be/YH_4c0K7fGM?si=pPc8iLI2Y9FGD3yo&t=385) – Create and run a [Realtime](/realtime/overview) example using [React hooks](/frontend/react-hooks) – ([View the code](/guides/examples/fal-ai-realtime))
|
||||
- [6:25](https://youtu.be/YH_4c0K7fGM?si=pPc8iLI2Y9FGD3yo&t=385) – Create and run a [Realtime](/realtime/overview) example using [React hooks](/realtime/react-hooks) – ([View the code](/guides/examples/fal-ai-realtime))
|
||||
- [11:10](https://youtu.be/YH_4c0K7fGM?si=Mjd0EvvNsNlVouvY&t=670) – [Deploy your task](/cli-deploy) to the Trigger.dev Cloud
|
||||
|
||||
@@ -125,7 +125,7 @@ The JSON body of the POST request will be used as the output of the token. If th
|
||||
|
||||
<ParamField query="publicAccessToken" type="string">
|
||||
A Public Access Token that can be used to complete the token from a client-side application (or
|
||||
another backend). See our [frontend docs](/frontend/overview) for more details.
|
||||
another backend). See our [Realtime docs](/realtime/auth) for more details.
|
||||
</ParamField>
|
||||
|
||||
### Example
|
||||
|
||||
Reference in New Issue
Block a user