Skip to content

v3: update trigger API design #1045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .changeset/shaggy-spoons-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/core": patch
---

Updates the `trigger`, `batchTrigger` and their `*AndWait` variants to use the first parameter for the payload/items, and the second parameter for options.

Before:

```ts
await yourTask.trigger({ payload: { foo: "bar" }, options: { idempotencyKey: "key_1234" } });
await yourTask.triggerAndWait({ payload: { foo: "bar" }, options: { idempotencyKey: "key_1234" } });

await yourTask.batchTrigger({ items: [{ payload: { foo: "bar" } }, { payload: { foo: "baz" } }] });
await yourTask.batchTriggerAndWait({ items: [{ payload: { foo: "bar" } }, { payload: { foo: "baz" } }] });
```

After:

```ts
await yourTask.trigger({ foo: "bar" }, { idempotencyKey: "key_1234" });
await yourTask.triggerAndWait({ foo: "bar" }, { idempotencyKey: "key_1234" });

await yourTask.batchTrigger([{ payload: { foo: "bar" } }, { payload: { foo: "baz" } }]);
await yourTask.batchTriggerAndWait([{ payload: { foo: "bar" } }, { payload: { foo: "baz" } }]);
```

We've also changed the API of the `triggerAndWait` result. Before, if the subtask that was triggered finished with an error, we would automatically "rethrow" the error in the parent task.

Now instead we're returning a `TaskRunResult` object that allows you to discriminate between successful and failed runs in the subtask:

Before:

```ts
try {
const result = await yourTask.triggerAndWait({ foo: "bar" });

// result is the output of your task
console.log("result", result);

} catch (error) {
// handle subtask errors here
}
```

After:

```ts
const result = await yourTask.triggerAndWait({ foo: "bar" });

if (result.ok) {
console.log(`Run ${result.id} succeeded with output`, result.output);
} else {
console.log(`Run ${result.id} failed with error`, result.error);
}
```
2 changes: 1 addition & 1 deletion docs/v3/errors-retrying.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const myTask = task({
maxAttempts: 10,
},
run: async (payload: string) => {
const result = await otherTask.triggerAndWait({ payload: "some data" });
const result = await otherTask.triggerAndWait("some data");
//...do other stuff
},
});
Expand Down
6 changes: 3 additions & 3 deletions docs/v3/migration-defer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@ export async function runLongRunningTask() {
}
```

In Trigger.dev your logic goes in the `run` function of a task. You can then `trigger` and `batchTrigger` that task, with a payload and options.
In Trigger.dev your logic goes in the `run` function of a task. You can then `trigger` and `batchTrigger` that task, with a payload as the first argument.

```ts /app/actions/actions.ts
"use server";

import { longRunningTask } from "@/trigger/someTasks";

export async function runLongRunningTask() {
return await longRunningTask.trigger({ payload: { foo: "bar" } });
return await longRunningTask.trigger({ foo: "bar" });
}
```

Expand Down Expand Up @@ -243,7 +243,7 @@ export const longRunningTask = task({
import { longRunningTask } from "@/trigger/longRunningTask";

export async function runLongRunningTask() {
return await longRunningTask.trigger({ payload: { foo: "bar" } });
return await longRunningTask.trigger({ foo: "bar" });
}
```

Expand Down
49 changes: 19 additions & 30 deletions docs/v3/queue-concurrency.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,19 @@ export async function POST(request: Request) {

if (data.branch === "main") {
//trigger the task, with a different queue
const handle = await generatePullRequest.trigger({
payload: data,
options: {
queue: {
//the "main-branch" queue will have a concurrency limit of 10
//this triggered run will use that queue
name: "main-branch",
concurrencyLimit: 10,
},
const handle = await generatePullRequest.trigger(data, {
queue: {
//the "main-branch" queue will have a concurrency limit of 10
//this triggered run will use that queue
name: "main-branch",
concurrencyLimit: 10,
},
});

return Response.json(handle);
} else {
//triggered with the default (concurrency of 1)
const handle = await generatePullRequest.trigger({
payload: data,
});
const handle = await generatePullRequest.trigger(data);
return Response.json(handle);
}
}
Expand All @@ -146,32 +141,26 @@ export async function POST(request: Request) {

if (data.isFreeUser) {
//free users can only have 1 PR generated at a time
const handle = await generatePullRequest.trigger({
payload: data,
options: {
queue: {
//every free user gets a queue with a concurrency limit of 1
name: "free-users",
concurrencyLimit: 1,
},
concurrencyKey: data.userId,
const handle = await generatePullRequest.trigger(data, {
queue: {
//every free user gets a queue with a concurrency limit of 1
name: "free-users",
concurrencyLimit: 1,
},
concurrencyKey: data.userId,
});

//return a success response with the handle
return Response.json(handle);
} else {
//trigger the task, with a different queue
const handle = await generatePullRequest.trigger({
payload: data,
options: {
queue: {
//every paid user gets a queue with a concurrency limit of 10
name: "paid-users",
concurrencyLimit: 10,
},
concurrencyKey: data.userId,
const handle = await generatePullRequest.trigger(data, {
queue: {
//every paid user gets a queue with a concurrency limit of 10
name: "paid-users",
concurrencyLimit: 10,
},
concurrencyKey: data.userId,
});

//return a success response with the handle
Expand Down
2 changes: 1 addition & 1 deletion docs/v3/tasks-overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { helloWorldTask } from "./trigger/hello-world";

async function triggerHelloWorld() {
//This triggers the task and return a handle
const handle = await helloWorld.trigger({ payload: { message: "Hello world!" } });
const handle = await helloWorld.trigger({ message: "Hello world!" });

//You can use the handle to check the status of the task, cancel and retry it.
console.log("Task is running with handle", handle.id);
Expand Down
65 changes: 36 additions & 29 deletions docs/v3/triggering.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export async function POST(request: Request) {
const data = await request.json();

//trigger your task
const handle = await emailSequence.trigger({ payload: { to: data.email, name: data.name } });
const handle = await emailSequence.trigger({ to: data.email, name: data.name });

//return a success response with the handle
return Response.json(handle);
Expand All @@ -67,7 +67,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
const data = await request.json();

//trigger your task
const handle = await emailSequence.trigger({ payload: { to: data.email, name: data.name } });
const handle = await emailSequence.trigger({ to: data.email, name: data.name });

//return a success response with the handle
return json(handle);
Expand All @@ -91,9 +91,9 @@ export async function POST(request: Request) {
const data = await request.json();

//batch trigger your task
const batchHandle = await emailSequence.batchTrigger({
items: data.users.map((u) => ({ payload: { to: u.email, name: u.name } })),
});
const batchHandle = await emailSequence.batchTrigger(
data.users.map((u) => ({ payload: { to: u.email, name: u.name } }))
);

//return a success response with the handle
return Response.json(batchHandle);
Expand All @@ -112,9 +112,9 @@ export async function action({ request, params }: ActionFunctionArgs) {
const data = await request.json();

//batch trigger your task
const batchHandle = await emailSequence.batchTrigger({
items: data.users.map((u) => ({ payload: { to: u.email, name: u.name } })),
});
const batchHandle = await emailSequence.batchTrigger(
data.users.map((u) => ({ payload: { to: u.email, name: u.name } }))
);

//return a success response with the handle
return json(batchHandle);
Expand All @@ -137,7 +137,7 @@ import { myOtherTask } from "~/trigger/my-other-task";
export const myTask = task({
id: "my-task",
run: async (payload: string) => {
const handle = await myOtherTask.trigger({ payload: "some data" });
const handle = await myOtherTask.trigger("some data");

//...do other stuff
},
Expand All @@ -154,7 +154,7 @@ import { myOtherTask } from "~/trigger/my-other-task";
export const myTask = task({
id: "my-task",
run: async (payload: string) => {
const batchHandle = await myOtherTask.batchTrigger({ items: [{ payload: "some data" }] });
const batchHandle = await myOtherTask.batchTrigger([{ payload: "some data" }]);

//...do other stuff
},
Expand All @@ -168,16 +168,18 @@ This is where it gets interesting. You can trigger a task and then wait for the
<Accordion title="Don't use this in parallel, e.g. with `Promise.all()`">
Instead, use `batchTriggerAndWait()` if you can, or a for loop if you can't.

To control concurrency using batch triggers, you can set `queue.concurrencyLimit` on the child task.
To control concurrency using batch triggers, you can set `queue.concurrencyLimit` on the child task.

<CodeGroup>

```ts /trigger/batch.ts
export const batchTask = task({
id: "batch-task",
run: async (payload: string) => {
const results = await childTask.batchTriggerAndWait({
items: [{ payload: "item1" }, { payload: "item2" }],
});
const results = await childTask.batchTriggerAndWait([
{ payload: "item1" },
{ payload: "item2" },
]);
console.log("Results", results);

//...do stuff with the results
Expand All @@ -192,14 +194,15 @@ export const loopTask = task({
//this will be slower than the batch version
//as we have to resume the parent after each iteration
for (let i = 0; i < 2; i++) {
const result = await childTask.triggerAndWait({ payload: `item${i}` });
const result = await childTask.triggerAndWait(`item${i}`);
console.log("Result", result);

//...do stuff with the result
}
},
});
```

</CodeGroup>

</Accordion>
Expand All @@ -208,7 +211,7 @@ export const loopTask = task({
export const parentTask = task({
id: "parent-task",
run: async (payload: string) => {
const result = await batchChildTask.triggerAndWait({ payload: "some-data" });
const result = await batchChildTask.triggerAndWait("some-data");
console.log("Result", result);

//...do stuff with the result
Expand All @@ -223,16 +226,18 @@ You can batch trigger a task and wait for all the results. This is useful for th
<Accordion title="Don't use this in parallel, e.g. with `Promise.all()`">
Instead, pass in all items at once and set an appropriate `maxConcurrency`. Alternatively, use sequentially with a for loop.

To control concurrency, you can set `queue.concurrencyLimit` on the child task.
To control concurrency, you can set `queue.concurrencyLimit` on the child task.

<CodeGroup>

```ts /trigger/batch.ts
export const batchTask = task({
id: "batch-task",
run: async (payload: string) => {
const results = await childTask.batchTriggerAndWait({
items: [{ payload: "item1" }, { payload: "item2" }],
});
const results = await childTask.batchTriggerAndWait([
{ payload: "item1" },
{ payload: "item2" },
]);
console.log("Results", results);

//...do stuff with the results
Expand All @@ -247,16 +252,18 @@ export const loopTask = task({
//this will be slower than a single batchTriggerAndWait()
//as we have to resume the parent after each iteration
for (let i = 0; i < 2; i++) {
const result = await childTask.batchTriggerAndWait({
items: [{ payload: `itemA${i}` }, { payload: `itemB${i}` }],
});
const result = await childTask.batchTriggerAndWait([
{ payload: `itemA${i}` },
{ payload: `itemB${i}` },
]);
console.log("Result", result);

//...do stuff with the result
}
},
});
```

</CodeGroup>

</Accordion>
Expand All @@ -265,9 +272,11 @@ export const loopTask = task({
export const batchParentTask = task({
id: "parent-task",
run: async (payload: string) => {
const results = await childTask.batchTriggerAndWait({
items: [{ payload: "item4" }, { payload: "item5" }, { payload: "item6" }],
});
const results = await childTask.batchTriggerAndWait([
{ payload: "item4" },
{ payload: "item5" },
{ payload: "item6" },
]);
console.log("Results", results);

//...do stuff with the result
Expand Down Expand Up @@ -326,9 +335,7 @@ import { createAvatar } from "@/trigger/create-avatar";
export async function create() {
try {
const handle = await createAvatar.trigger({
payload: {
userImage: "http://...",
},
userImage: "http://...",
});

return { handle };
Expand Down
4 changes: 1 addition & 3 deletions docs/v3/upgrading-from-v2.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,7 @@ We've unified triggering in v3. You use `trigger()` or `batchTrigger()` which yo
async function yourBackendFunction() {
//call `trigger()` on any task
const handle = await openaiTask.trigger({
payload: {
prompt: "Tell me a programming joke",
},
prompt: "Tell me a programming joke",
});
}
```
Expand Down
Loading