December 4, 2024
Trigger from the frontend
Trigger.dev now supports triggering tasks from the frontend.
Introducing new React hooks for triggering Trigger.dev tasks directly from your frontend applications. Here's what's new:
- The
@trigger.dev/react-hooks
has been updated to include new "trigger" hooks that allow developers to execute tasks directly from their frontend applications. - Built-in security through one-time use "trigger" tokens ensures safe task execution, with customizable expiration times.
- Integrates with Realtime API to make it easy to trigger tasks and receive updates in real-time.
This allows you to simplify your backend code to go from this:
To this:
Demo
We built a demo application to showcase how you can use the new hooks to trigger tasks directly from your frontend application and then receive updates in real-time:
In this demo:
- A Next.js application that uses the new
@trigger.dev/react-hooks
to trigger a task. - A background task that sends a prompt to two different LLM models using the AI SDK and streams their responses back to the frontend in real-time.
- The background task then evaluates the responses and sends the results back to the frontend in real-time, declaring a winner.
You can find the source code for this demo application here.
Features
One time use "trigger" tokens
We've added a new type of Public Access Token that can be used to trigger tasks directly from your frontend applications. These tokens are one-time use and can be configured to expire after a certain amount of time, as well as be restricted to specific tasks:
import { auth } from "@trigger.dev/sdk/v3";// This should happen in your backendconst triggerToken = await auth.createTriggerPublicToken("my-task");// Now pass your triggerToken to your frontend// ...
By default, these tokens expire after 15 minutes, but you can customize the expiration time:
const triggerToken = await auth.createTriggerPublicToken("my-task", { expirationTime: "24hr",});
See the docs for more information.
useTaskTrigger hook
If you just want to trigger a task and don't need to listen for updates, you can use the useTaskTrigger
hook:
"use client"; // This is needed for Next.js App Router or other RSC frameworksimport { useTaskTrigger } from "@trigger.dev/react-hooks";import type { myTask } from "@/trigger/myTask";// 👆 This is the type of your taskexport function TriggerTask({ token }: { token: string }) { // pass the type of your task here 👇 const { submit, handle, error, isLoading } = useTaskTrigger<typeof myTask>( "my-task", { accessToken: token, // 👈 this is the "trigger" token } ); if (error) { return <div>Error: {error.message}</div>; } if (handle) { return <div>Run ID: {handle.id}</div>; } // submit with a typesafe payload 👇 return ( <button onClick={() => submit({ foo: "bar" })} disabled={isLoading}> {isLoading ? "Loading..." : "Trigger Task"} </button> );}
useRealtimeTaskTrigger
If you want to trigger a task and listen for updates in real-time, you can use the useRealtimeTaskTrigger
hook:
"use client"; // This is needed for Next.js App Router or other RSC frameworksimport { useRealtimeTaskTrigger } from "@trigger.dev/react-hooks";import type { myTask } from "@/trigger/myTask";export function TriggerRealtimeTask({ token }: { token: string }) { const { submit, run, error, isLoading } = useRealtimeTaskTrigger< typeof myTask >("my-task", { accessToken: token, }); if (error) { return <div>Error: {error.message}</div>; } // This is the realtime run object, which will automatically update when the run changes if (run) { return <div>Run ID: {run.id}</div>; } return ( <button onClick={() => submit({ foo: "bar" })} disabled={isLoading}> {isLoading ? "Loading..." : "Trigger Task"} </button> );}
useRealtimeTaskTriggerWithStreams
If you want to trigger a task and listen for updates in real-time, including the ability to listen to streams, you can use the useRealtimeTaskTriggerWithStreams
hook:
"use client"; // This is needed for Next.js App Router or other RSC frameworksimport { 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 <div>Error: {error.message}</div>; } if (streams && run) { const text = streams.openai?.map((part) => part).join(""); return ( <div> <div>Run ID: {run.id}</div> <div>{text}</div> </div> ); } return ( <button onClick={() => submit({ foo: "bar" })} disabled={isLoading}> {isLoading ? "Loading..." : "Trigger Task"} </button> );}
For more on how to use these hooks, see the docs.
Using one-time use tokens in other contexts
One-time use tokens can be used in any context where you need to trigger a task without exposing your API key, not just in frontend applications. For example, imagine you have a CLI tool that needs to trigger a task:
import { auth, tasks, runs } from "@trigger.dev/sdk/v3";// This would get generated in your backend and passed to your CLI toolconst triggerToken = await auth.createTriggerPublicToken("create-project");// In your CLI tool, authenticate with the trigger tokenawait auth.withAuth({ accessToken: triggerToken }, async () => { const handle = await tasks.trigger("create-project", { name: "My Project", description: "A new project", }); // Now authenticate with the PAT that has access to the run await auth.withAuth({ accessToken: handle.publicAccessToken }, async () => { // Now subscribe to the run for await (const run of runs.subscribe(handle.id)) { // Do something with the run console.log(run); } // The run is complete });});
Multiple-use single use tokens
If you need to trigger a task multiple times with the same token, you can use the multipleUse
option:
const triggerToken = await auth.createTriggerPublicToken("my-task", { multipleUse: true, // 👈 this allows the token to be used multiple times});
Just be aware that this is less secure than one-time use tokens, as the token can be used multiple times and will only be invalidated when it expires.
Getting started
Head over to our React hooks documentation to get started with the new hooks.
As always, we're here to help if you have any questions or need assistance. You can reach out to us on Discord or X.