diff --git a/.changeset/khaki-ants-yawn.md b/.changeset/khaki-ants-yawn.md new file mode 100644 index 0000000000..3f37054c76 --- /dev/null +++ b/.changeset/khaki-ants-yawn.md @@ -0,0 +1,7 @@ +--- +"@trigger.dev/react-hooks": patch +"@trigger.dev/sdk": patch +--- + +React hooks now all accept accessToken and baseURL options so the use of the Provider is no longer necessary + diff --git a/docs/frontend/overview.mdx b/docs/frontend/overview.mdx index 4f48d9695a..242974fc38 100644 --- a/docs/frontend/overview.mdx +++ b/docs/frontend/overview.mdx @@ -4,31 +4,16 @@ sidebarTitle: Overview & Auth description: Using the Trigger.dev SDK from your frontend application. --- -You can use certain SDK functions in your frontend application to interact with the Trigger.dev API. This guide will show you how to authenticate your requests and use the SDK in 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 -You must authenticate your requests using a "Public Access Token" when using the SDK in your frontend application. To create a Public Access Token, you can use the `auth.createPublicToken` function in your backend code: +To create a Public Access Token, you can use the `auth.createPublicToken` function in your **backend** code: ```tsx const publicToken = await auth.createPublicToken(); ``` -To use a Public Access Token in your frontend application, you can call the `auth.configure` function or the `auth.withAuth` function: - -```ts -import { auth } from "@trigger.dev/sdk/v3"; - -auth.configure({ - accessToken: publicToken, -}); - -// or -await auth.withAuth({ accessToken: publicToken }, async () => { - // Your code here will use the public token -}); -``` - ### Scopes By default a Public Access Token has limited permissions. You can specify the scopes you need when creating a Public Access Token: @@ -104,6 +89,22 @@ const publicToken = await auth.createPublicToken({ }); ``` +### Write scopes + +You can also specify write scopes, which is required for triggering tasks from your frontend application: + +```ts +const publicToken = await auth.createPublicToken({ + scopes: { + write: { + tasks: ["my-task-1", "my-task-2"], + }, + }, +}); +``` + +This will allow the token to trigger the specified tasks. `tasks` is the only write scope available at the moment. + ### Expiration By default, Public Access Token's expire after 15 minutes. You can specify a different expiration time when creating a Public Access Token: @@ -163,80 +164,6 @@ const handle = await tasks.batchTrigger("my-task", [ console.log(handle.publicAccessToken); ``` -## Available SDK functions - -Currently the following functions are available in the frontend SDK: - -### runs.retrieve - -The `runs.retrieve` function allows you to retrieve a run by its ID. - -```ts -import { runs, auth } from "@trigger.dev/sdk/v3"; - -// Somewhere in your backend code -const handle = await tasks.trigger("my-task", { some: "data" }); - -// In your frontend code -auth.configure({ - accessToken: handle.publicAccessToken, -}); - -const run = await runs.retrieve(handle.id); -``` - -Learn more about the `runs.retrieve` function in the [runs.retrieve doc](/management/runs/retrieve). - -### runs.subscribeToRun - -The `runs.subscribeToRun` function allows you to subscribe to a run by its ID, and receive updates in real-time when the run changes. - -```ts -import { runs, auth } from "@trigger.dev/sdk/v3"; - -// Somewhere in your backend code -const handle = await tasks.trigger("my-task", { some: "data" }); - -// In your frontend code -auth.configure({ - accessToken: handle.publicAccessToken, -}); - -for await (const run of runs.subscribeToRun(handle.id)) { - // This will log the run every time it changes - console.log(run); -} -``` - -See the [Realtime doc](/realtime) for more information. - -### runs.subscribeToRunsWithTag - -The `runs.subscribeToRunsWithTag` function allows you to subscribe to runs with a specific tag, and receive updates in real-time when the runs change. - -```ts -import { runs, auth } from "@trigger.dev/sdk/v3"; - -// Somewhere in your backend code -const handle = await tasks.trigger("my-task", { some: "data" }, { tags: ["my-tag"] }); - -// In your frontend code -auth.configure({ - accessToken: handle.publicAccessToken, -}); - -for await (const run of runs.subscribeToRunsWithTag("my-tag")) { - // This will log the run every time it changes - console.log(run); -} -``` - -See the [Realtime doc](/realtime) for more information. - -## React hooks - -We also provide React hooks to make it easier to use the SDK in your React application. See our [React hooks](/frontend/react-hooks) documentation for more information. - -## Triggering tasks +## Usage -We don't currently support triggering tasks from the frontend SDK. If this is something you need, please let us know by [upvoting the feature](https://feedback.trigger.dev/p/ability-to-trigger-tasks-from-frontend). +To learn how to use these Public Access Tokens, see our [React hooks](/frontend/react-hooks) guide. diff --git a/docs/frontend/react-hooks.mdx b/docs/frontend/react-hooks.mdx index 828979cada..5ad9677c72 100644 --- a/docs/frontend/react-hooks.mdx +++ b/docs/frontend/react-hooks.mdx @@ -28,14 +28,35 @@ yarn install @trigger.dev/react-hooks ## Authentication -Before you can use the hooks, you need to provide a public access token to the `TriggerAuthContext` provider. Learn more about [authentication in the frontend guide](/frontend/overview). +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() { +export function SetupTrigger({ publicAccessToken }: { publicAccessToken: string }) { return ( - + ); @@ -47,11 +68,11 @@ Now children components can use the hooks to interact with the Trigger.dev API. ```tsx import { TriggerAuthContext } from "@trigger.dev/react-hooks"; -export function SetupTrigger() { +export function SetupTrigger({ publicAccessToken }: { publicAccessToken: string }) { return ( @@ -217,9 +238,7 @@ export async function generatePublicAccessToken(runId: string) { -## Usage - -### SWR vs Realtime hooks +## 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. @@ -231,77 +250,7 @@ We offer two "styles" of hooks: SWR and Realtime. The SWR hooks use the [swr](ht All hooks named `useRealtime*` are Realtime hooks, and all hooks named `use*` are SWR hooks. -#### Common SWR hook options - -You can pass the following options to the all SWR hooks: - - - Revalidate the data when the window regains focus. - - - - Revalidate the data when the browser regains a network connection. - - - - Poll for updates at the specified interval (in milliseconds). Polling is not recommended for most - use-cases. Use the Realtime hooks instead. - - -#### Common SWR hook return values - - - An error object if an error occurred while fetching the data. - - - - A boolean indicating if the data is currently being fetched. - - - - A boolean indicating if the data is currently being revalidated. - - - - A boolean indicating if an error occurred while fetching the data. - - -### 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
Loading...
; - if (error) return
Error: {error.message}
; - - return
Run: {run.id}
; -} -``` - -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(runId); - - if (isLoading) return
Loading...
; - if (error) return
Error: {error.message}
; - - // Now run.payload and run.output are correctly typed - - return
Run: {run.id}
; -} -``` +## Realtime hooks ### useRealtimeRun @@ -312,8 +261,16 @@ The `useRealtimeRun` hook allows you to subscribe to a run by its ID. import { useRealtimeRun } from "@trigger.dev/react-hooks"; -export function MyComponent({ runId }: { runId: string }) { - const { run, error } = useRealtimeRun(runId); +export function MyComponent({ + runId, + publicAccessToken, +}: { + runId: string; + publicAccessToken: string; +}) { + const { run, error } = useRealtimeRun(runId, { + accessToken: publicAccessToken, + }); if (error) return
Error: {error.message}
; @@ -327,8 +284,16 @@ To correctly type the run's payload and output, you can provide the type of your import { useRealtimeRun } from "@trigger.dev/react-hooks"; import type { myTask } from "@/trigger/myTask"; -export function MyComponent({ runId }: { runId: string }) { - const { run, error } = useRealtimeRun(runId); +export function MyComponent({ + runId, + publicAccessToken, +}: { + runId: string; + publicAccessToken: string; +}) { + const { run, error } = useRealtimeRun(runId, { + accessToken: publicAccessToken, + }); if (error) return
Error: {error.message}
; @@ -338,7 +303,7 @@ export function MyComponent({ runId }: { runId: string }) { } ``` -See our [Realtime documentation](/realtime) for more information. +See our [Realtime documentation](/realtime) for more information about the type of the run object and more. ### useRealtimeRunsWithTag @@ -444,3 +409,389 @@ export function MyComponent({ batchId }: { batchId: string }) { ``` 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
Error: {error.message}
; + + return ( +
+
Run: {run.id}
+
+ {Object.keys(streams).map((stream) => ( +
Stream: {stream}
+ ))} +
+
+ ); +} +``` + +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(runId, { + accessToken: publicAccessToken, + }); + + if (error) return
Error: {error.message}
; + + const text = streams.openai?.map((part) => part).join(""); + + return ( +
+
Run: {run.id}
+
{text}
+
+ ); +} +``` + +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(runId, { + accessToken: publicAccessToken, + }); + + if (error) return
Error: {error.message}
; + + const text = streams.openai + ?.filter((stream) => stream.type === "text-delta") + ?.map((part) => part.text) + .join(""); + + return ( +
+
Run: {run.id}
+
{text}
+
+ ); +} +``` + +### Common options + +#### 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
Error: {error.message}
; + + return
Run: {run.id}
; +} +``` + +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
Error: {error.message}
; + + return
Run: {run.id}
; +} +``` + +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
Error: {error.message}
; + + return ( +
+ {runs.map((run) => ( +
Run: {run.id}
+ ))} +
+ ); +} +``` + +## 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
Loading...
; + if (error) return
Error: {error.message}
; + + return
Run: {run.id}
; +} +``` + +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(runId, { + refreshInterval: 0, // Disable polling + }); + + if (isLoading) return
Loading...
; + if (error) return
Error: {error.message}
; + + // Now run.payload and run.output are correctly typed + + return
Run: {run.id}
; +} +``` + +### Common options + +You can pass the following options to the all SWR hooks: + + + Revalidate the data when the window regains focus. + + + + Revalidate the data when the browser regains a network connection. + + + + Poll for updates at the specified interval (in milliseconds). Polling is not recommended for most + use-cases. Use the Realtime hooks instead. + + +### Common return values + + + An error object if an error occurred while fetching the data. + + + + A boolean indicating if the data is currently being fetched. + + + + A boolean indicating if the data is currently being revalidated. + + + + A boolean indicating if an error occurred while fetching the data. + + +## Trigger Hooks + +We provide a set of hooks that can be used to trigger tasks from your frontend application. You'll need to generate a Public Access Token with `write` permissions to use these hooks. See our [frontend guide](/frontend/overview#write-scopes) for more information. + +### useTaskTrigger + +The `useTaskTrigger` hook allows you to trigger a task from your frontend application. + +```tsx +"use client"; // This is needed for Next.js App Router or other RSC frameworks + +import { useTaskTrigger } from "@trigger.dev/react-hooks"; +import type { myTask } from "@/trigger/myTask"; + +export function MyComponent({ publicAccessToken }: { publicAccessToken: string }) { + const { submit, handle, error, isLoading } = useTaskTrigger("my-task", { + accessToken: publicAccessToken, + }); + + if (error) { + return
Error: {error.message}
; + } + + if (handle) { + return
Run ID: {handle.id}
; + } + + return ( + + ); +} +``` + +### useRealtimeTaskTrigger + +The `useRealtimeTaskTrigger` hook allows you to trigger a task from your frontend application and then subscribe to the run in using Realtime: + +```tsx +"use client"; // This is needed for Next.js App Router or other RSC frameworks + +import { useRealtimeTaskTrigger } from "@trigger.dev/react-hooks"; +import type { myTask } from "@/trigger/myTask"; + +export function MyComponent({ publicAccessToken }: { publicAccessToken: string }) { + const { submit, run, error, isLoading } = useRealtimeTaskTrigger("my-task", { + accessToken: publicAccessToken, + }); + + if (error) { + return
Error: {error.message}
; + } + + // This is the realtime run object, which will automatically update when the run changes + if (run) { + return
Run ID: {run.id}
; + } + + return ( + + ); +} +``` + +### useRealtimeTaskTriggerWithStreams + +The `useRealtimeTaskTriggerWithStreams` hook allows you to trigger a task from your frontend application and then subscribe to the run in using Realtime, 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 { useRealtimeTaskTriggerWithStreams } 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({ publicAccessToken }: { publicAccessToken: string }) { + const { submit, run, streams, error, isLoading } = useRealtimeTaskTriggerWithStreams< + typeof myTask, + STREAMS + >("my-task", { + accessToken: publicAccessToken, + }); + + if (error) { + return
Error: {error.message}
; + } + + if (streams && run) { + const text = streams.openai?.map((part) => part).join(""); + + return ( +
+
Run ID: {run.id}
+
{text}
+
+ ); + } + + return ( + + ); +} +``` diff --git a/docs/introduction.mdx b/docs/introduction.mdx index 9d3885ede2..2d42c1f7f3 100644 --- a/docs/introduction.mdx +++ b/docs/introduction.mdx @@ -12,13 +12,23 @@ Trigger.dev v3 makes it easy to write reliable long-running tasks without timeou - We provide an SDK and CLI for writing tasks in your existing codebase, inside [/trigger folders](/config/config-file). - We provide different types of tasks: [regular](/tasks-regular) and [scheduled](/tasks/scheduled). - We provide a dashboard for monitoring, debugging, and managing your tasks. +- We provide a [Realtime API](/realtime) for monitoring tasks in real-time, along with [React hooks](/frontend/react-hooks#realtime-hooks) for building custom dashboards. We're [open source](https://github.com/triggerdotdev/trigger.dev) and you can choose to use the [Trigger.dev Cloud](https://cloud.trigger.dev) or [Self-host Trigger.dev](/open-source-self-hosting) on your own infrastructure. ## Getting started
-