Improved typesafe task triggering

Matt AitkenMatt Aitken

Image forImproved typesafe task triggering

Currently the only way to trigger a task in a typesafe way is to use the Task.trigger instance method:

Next.js

_17
import { emailSequence } from "~/trigger/emails";
_17
// 👆 import the emailSequence task
_17
_17
//app/email/route.ts
_17
export async function POST(request: Request) {
_17
//get the JSON from the request
_17
const data = await request.json();
_17
_17
//trigger your task
_17
const handle = await emailSequence.trigger({
_17
to: data.email,
_17
name: data.name,
_17
});
_17
_17
//return a success response with the handle
_17
return Response.json(handle);
_17
}

This is great for most cases, but importing the task means importing anything the task depends on. This can lead to bloated applications, or worse, build failures because of some esoteric dependency that doesn't work when included in a Next.js 14.2.4 project (for example).

As of v3.0.0-beta.41, you can now trigger tasks using the tasks.trigger SDK function, passing in the type of the task only as a generic parameter:

Next.js

_19
import { tasks } from "@trigger.dev/sdk/v3";
_19
import type { emailSequence } from "~/trigger/emails";
_19
// 👆 **type-only** import
_19
_19
//app/email/route.ts
_19
export async function POST(request: Request) {
_19
//get the JSON from the request
_19
const data = await request.json();
_19
_19
// Pass the task type to `trigger()` as a generic argument, giving you full type checking
_19
const handle = await tasks.trigger<typeof emailSequence>("email-sequence", {
_19
// 👆 this ID is fully type safe as well
_19
to: data.email,
_19
name: data.name,
_19
});
_19
_19
//return a success response with the handle
_19
return Response.json(handle);
_19
}

By importing only the type of the task, any dependencies the task has are not included in the build, and you get full type checking on the task payload, ID, and output.

This works with batchTrigger as well:

Next.js

_18
import { tasks } from "@trigger.dev/sdk/v3";
_18
import type { emailSequence } from "~/trigger/emails";
_18
// 👆 **type-only** import
_18
_18
//app/email/route.ts
_18
export async function POST(request: Request) {
_18
//get the JSON from the request
_18
const data = await request.json();
_18
_18
// Pass the task type to `batchTrigger()` as a generic argument, giving you full type checking
_18
const batchHandle = await tasks.batchTrigger<typeof emailSequence>(
_18
"email-sequence",
_18
data.users.map((u) => ({ payload: { to: u.email, name: u.name } }))
_18
);
_18
_18
//return a success response with the handle
_18
return Response.json(batchHandle);
_18
}

In addition, the handle returned from trigger and batchTrigger now carries extra type information about the output of the task, which can be used to retrieve the run output in a typesafe way:


_15
import { tasks, runs } from "@trigger.dev/sdk/v3";
_15
import type { emailSequence } from "~/trigger/emails";
_15
// 👆 **type-only** import
_15
_15
async function main(email: string, name: string) {
_15
// Pass the task type to `trigger()` as a generic argument, giving you full type checking
_15
const handle = await tasks.trigger<typeof emailSequence>("email-sequence", {
_15
to: email,
_15
name,
_15
});
_15
_15
const run = await runs.retrieve(handle);
_15
_15
return run.output; // 👈 run.output is now typed
_15
}

There's more! We included a polling helper function to the runs SDK module, which can be used to poll for the completion of a task run:


_16
import { tasks, runs } from "@trigger.dev/sdk/v3";
_16
import type { emailSequence } from "~/trigger/emails";
_16
// 👆 **type-only** import
_16
_16
async function main(email: string, name: string) {
_16
// Pass the task type to `trigger()` as a generic argument, giving you full type checking
_16
const handle = await tasks.trigger<typeof emailSequence>("email-sequence", {
_16
to: email,
_16
name,
_16
});
_16
_16
// Poll for the completion of the task run
_16
const run = await runs.poll(handle, { pollIntervalMs: 1000 });
_16
_16
return run.output; // 👈 run.output is now typed and complete
_16
}

We've also added a triggerAndPoll helper function to the tasks SDK module, which can be used to trigger a task and poll for the completion of the task run in one go:


_18
import { tasks } from "@trigger.dev/sdk/v3";
_18
import type { emailSequence } from "~/trigger/emails";
_18
_18
async function main(email: string, name: string) {
_18
// Pass the task type to `triggerAndPoll()` as a generic argument, giving you full type checking
_18
const run = await tasks.triggerAndPoll<typeof emailSequence>(
_18
"email-sequence",
_18
{
_18
to: email,
_18
name,
_18
},
_18
{
_18
pollIntervalMs: 1000,
_18
}
_18
);
_18
_18
return run.output; // 👈 run.output is now typed and complete
_18
}

NOTE

These new SDK functions are meant to be used from your server-side code only. They also aren't meant to replace Task.triggerAndWait or Task.batchTriggerAndWait when running inside another task.

You'll find these updates in the @trigger.dev/[email protected] package. Give it a try and let us know what you think, or if we've missed anything!

,