Skip to content

Commit c8585c7

Browse files
committed
Document triggerAndWait with unwrap and result types
1 parent c79bcc1 commit c8585c7

File tree

4 files changed

+114
-46
lines changed

4 files changed

+114
-46
lines changed

docs/triggering.mdx

Lines changed: 102 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ description: "Tasks need to be triggered in order to run."
55

66
Trigger tasks **from your backend**:
77

8-
| Function | This works | What it does |
9-
| -------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
10-
| `tasks.trigger()` | Anywhere | Triggers a task and gets a handle you can use to fetch and manage the run. [Read more](#tasks-trigger) |
11-
| `tasks.batchTrigger()` | Anywhere | Triggers a task multiple times and gets a handle you can use to fetch and manage the runs. [Read more](#tasks-batchtrigger) |
12-
| `tasks.triggerAndPoll()` | Anywhere | Triggers a task and then polls the run until it’s complete. [Read more](#tasks-triggerandpoll) |
8+
| Function | This works | What it does |
9+
| ------------------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------- |
10+
| `tasks.trigger()` | Anywhere | Triggers a task and gets a handle you can use to fetch and manage the run. [Read more](#tasks-trigger) |
11+
| `tasks.batchTrigger()` | Anywhere | Triggers a task multiple times and gets a handle you can use to fetch and manage the runs. [Read more](#tasks-batchtrigger) |
12+
| `tasks.triggerAndPoll()` | Anywhere | Triggers a task and then polls the run until it’s complete. [Read more](#tasks-triggerandpoll) |
1313

1414
Trigger tasks **from inside a run**:
1515

16-
| Function | This works | What it does |
17-
| -------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
18-
| `yourTask.trigger()` | Anywhere | Triggers a task and gets a handle you can use to monitor and manage the run. It does not wait for the result. [Read more](#task-trigger) |
19-
| `yourTask.batchTrigger()` | Anywhere | Triggers a task multiple times and gets a handle you can use to monitor and manage the runs. It does not wait for the results. [Read more](#task-batchtrigger) |
20-
| `yourTask.triggerAndWait()` | Inside task | Triggers a task and then waits until it's complete. You get the result data to continue with. [Read more](#task-triggerandwait) |
21-
| `yourTask.batchTriggerAndWait()` | Inside task | Triggers a task multiple times in parallel and then waits until they're all complete. You get the resulting data to continue with. [Read more](#task-batchtriggerandwait) |
16+
| Function | This works | What it does |
17+
| -------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
18+
| `yourTask.trigger()` | Anywhere | Triggers a task and gets a handle you can use to monitor and manage the run. It does not wait for the result. [Read more](#task-trigger) |
19+
| `yourTask.batchTrigger()` | Anywhere | Triggers a task multiple times and gets a handle you can use to monitor and manage the runs. It does not wait for the results. [Read more](#task-batchtrigger) |
20+
| `yourTask.triggerAndWait()` | Inside task | Triggers a task and then waits until it's complete. You get the result data to continue with. [Read more](#task-triggerandwait) |
21+
| `yourTask.batchTriggerAndWait()` | Inside task | Triggers a task multiple times in parallel and then waits until they're all complete. You get the resulting data to continue with. [Read more](#task-batchtriggerandwait) |
2222

2323
Additionally, [scheduled tasks](/tasks-scheduled) get **automatically** triggered on their schedule and webhooks when receiving a webhook.
2424

@@ -46,7 +46,9 @@ You can use Next.js Server Actions but [you need to be careful with bundling](/g
4646
Triggers a single run of a task with the payload you pass in, and any options you specify, without needing to import the task.
4747

4848
<Note>
49-
By using `tasks.trigger()`, you can pass in the task type as a generic argument, giving you full type checking. Make sure you use a `type` import so that your task code is not imported into your application.
49+
By using `tasks.trigger()`, you can pass in the task type as a generic argument, giving you full
50+
type checking. Make sure you use a `type` import so that your task code is not imported into your
51+
application.
5052
</Note>
5153

5254
<CodeGroup>
@@ -103,7 +105,9 @@ export async function action({ request, params }: ActionFunctionArgs) {
103105
Triggers multiples runs of a task with the payloads you pass in, and any options you specify, without needing to import the task.
104106

105107
<Note>
106-
By using `tasks.batchTrigger()`, you can pass in the task type as a generic argument, giving you full type checking. Make sure you use a `type` import so that your task code is not imported into your application.
108+
By using `tasks.batchTrigger()`, you can pass in the task type as a generic argument, giving you
109+
full type checking. Make sure you use a `type` import so that your task code is not imported into
110+
your application.
107111
</Note>
108112

109113
<CodeGroup>
@@ -159,7 +163,9 @@ export async function action({ request, params }: ActionFunctionArgs) {
159163
Triggers a single run of a task with the payload you pass in, and any options you specify, and then polls the run until it's complete.
160164

161165
<Note>
162-
By using `tasks.triggerAndPoll()`, you can pass in the task type as a generic argument, giving you full type checking. Make sure you use a `type` import so that your task code is not imported into your application.
166+
By using `tasks.triggerAndPoll()`, you can pass in the task type as a generic argument, giving you
167+
full type checking. Make sure you use a `type` import so that your task code is not imported into
168+
your application.
163169
</Note>
164170

165171
<CodeGroup>
@@ -313,14 +319,66 @@ export const loopTask = task({
313319
export const parentTask = task({
314320
id: "parent-task",
315321
run: async (payload: string) => {
316-
const result = await batchChildTask.triggerAndWait("some-data");
322+
const result = await childTask.triggerAndWait("some-data");
317323
console.log("Result", result);
318324

319325
//...do stuff with the result
320326
},
321327
});
322328
```
323329

330+
The `result` object is a "Result" type that needs to be checked to see if the child task run was successful:
331+
332+
```ts /trigger/parent.ts
333+
export const parentTask = task({
334+
id: "parent-task",
335+
run: async (payload: string) => {
336+
const result = await childTask.triggerAndWait("some-data");
337+
338+
if (result.ok) {
339+
console.log("Result", result.output); // result.output is the typed return value of the child task
340+
} else {
341+
console.error("Error", result.error); // result.error is the error that caused the run to fail
342+
}
343+
},
344+
});
345+
```
346+
347+
If instead you just want to get the output of the child task, and throw an error if the child task failed, you can use the `unwrap` method:
348+
349+
```ts /trigger/parent.ts
350+
export const parentTask = task({
351+
id: "parent-task",
352+
run: async (payload: string) => {
353+
const output = await childTask.triggerAndWait("some-data").unwrap();
354+
console.log("Output", output);
355+
},
356+
});
357+
```
358+
359+
You can also catch the error if the child task fails and get more information about the error:
360+
361+
```ts /trigger/parent.ts
362+
import { task, SubtaskUnwrapError } from "@trigger.dev/sdk/v3";
363+
export const parentTask = task({
364+
id: "parent-task",
365+
run: async (payload: string) => {
366+
try {
367+
const output = await childTask.triggerAndWait("some-data").unwrap();
368+
console.log("Output", output);
369+
} catch (error) {
370+
if (error instanceof SubtaskUnwrapError) {
371+
console.error("Error in fetch-post-task", {
372+
runId: error.runId,
373+
taskId: error.taskId,
374+
cause: error.cause,
375+
});
376+
}
377+
}
378+
},
379+
});
380+
```
381+
324382
<Warning>
325383
This method should only be used inside a task. If you use it outside a task, it will throw an
326384
error.
@@ -379,45 +437,45 @@ export const loopTask = task({
379437

380438
When using `batchTriggerAndWait`, you have full control over how to handle failures within the batch. The method returns an array of run results, allowing you to inspect each run's outcome individually and implement custom error handling.
381439

382-
Here's how you can manage run failures:
440+
Here's how you can manage run failures:
441+
442+
1. **Inspect individual run results**: Each run in the returned array has an `ok` property indicating success or failure.
383443

384-
1. **Inspect individual run results**: Each run in the returned array has an `ok` property indicating success or failure.
444+
2. **Access error information**: For failed runs, you can examine the `error` property to get details about the failure.
385445

386-
2. **Access error information**: For failed runs, you can examine the `error` property to get details about the failure.
446+
3. **Choose your failure strategy**: You have two main options:
387447

388-
3. **Choose your failure strategy**: You have two main options:
389-
- **Fail the entire batch**: Throw an error if any run fails, causing the parent task to reattempt.
390-
- **Continue despite failures**: Process the results without throwing an error, allowing the parent task to continue.
448+
- **Fail the entire batch**: Throw an error if any run fails, causing the parent task to reattempt.
449+
- **Continue despite failures**: Process the results without throwing an error, allowing the parent task to continue.
391450

392-
4. **Implement custom logic**: You can create sophisticated handling based on the number of failures, types of errors, or other criteria.
451+
4. **Implement custom logic**: You can create sophisticated handling based on the number of failures, types of errors, or other criteria.
393452

394-
Here's an example of how you might handle run failures:
453+
Here's an example of how you might handle run failures:
395454

396455
<CodeGroup>
397456

398457
```ts /trigger/batchTriggerAndWait.ts
399-
const result = await batchChildTask.batchTriggerAndWait([
400-
{ payload: "item1" },
401-
{ payload: "item2" },
402-
{ payload: "item3" },
403-
]);
404-
405-
// Result will contain the finished runs.
406-
// They're only finished if they have succeeded or failed.
407-
// "Failed" means all attempts failed
408-
409-
for (const run of result.runs) {
410-
411-
// Check if the run succeeded
412-
if (run.ok) {
413-
logger.info("Batch task run succeeded", { output: run.output });
414-
} else {
415-
logger.error("Batch task run error", { error: run.error });
458+
const result = await batchChildTask.batchTriggerAndWait([
459+
{ payload: "item1" },
460+
{ payload: "item2" },
461+
{ payload: "item3" },
462+
]);
463+
464+
// Result will contain the finished runs.
465+
// They're only finished if they have succeeded or failed.
466+
// "Failed" means all attempts failed
467+
468+
for (const run of result.runs) {
469+
// Check if the run succeeded
470+
if (run.ok) {
471+
logger.info("Batch task run succeeded", { output: run.output });
472+
} else {
473+
logger.error("Batch task run error", { error: run.error });
416474

417-
//You can choose if you want to throw an error and fail the entire run
418-
throw new Error(`Fail the entire run because ${run.id} failed`);
419-
}
475+
//You can choose if you want to throw an error and fail the entire run
476+
throw new Error(`Fail the entire run because ${run.id} failed`);
420477
}
478+
}
421479
```
422480

423481
</CodeGroup>
@@ -472,7 +530,7 @@ await myTask.trigger({ some: "data" }, { delay: "2024-12-01T00:00:00" });
472530
// Delay using a Date object
473531
await myTask.trigger({ some: "data" }, { delay: new Date(Date.now() + 1000 * 60 * 60) });
474532
// Delay using a timezone
475-
await myTask.trigger({ some: "data" }, { delay: new Date('2024-07-23T11:50:00+02:00') });
533+
await myTask.trigger({ some: "data" }, { delay: new Date("2024-07-23T11:50:00+02:00") });
476534
```
477535

478536
Runs that are delayed and have not been enqueued yet will display in the dashboard with a "Delayed" status:

packages/trigger-sdk/src/v3/shared.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ export type TaskRunResult<TOutput = any> =
266266
export class SubtaskUnwrapError extends Error {
267267
public readonly taskId: string;
268268
public readonly runId: string;
269+
public readonly cause?: unknown;
269270

270271
constructor(taskId: string, runId: string, subtaskError: unknown) {
271272
if (subtaskError instanceof Error) {

packages/trigger-sdk/src/v3/tasks.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import {
66
trigger,
77
triggerAndPoll,
88
triggerAndWait,
9+
SubtaskUnwrapError,
910
} from "./shared.js";
1011

12+
export { SubtaskUnwrapError };
13+
1114
import type {
1215
TaskOptions,
1316
Task,

references/v3-catalog/src/trigger/simple.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import "server-only";
2-
import { logger, task, tasks, wait } from "@trigger.dev/sdk/v3";
2+
import { logger, SubtaskUnwrapError, task, tasks, wait } from "@trigger.dev/sdk/v3";
33
import { traceAsync } from "@/telemetry.js";
44
import { HeaderGenerator } from "header-generator";
55

@@ -40,7 +40,13 @@ export const anyPayloadTask = task({
4040

4141
console.log("Result from fetch-post-task 211111sss", { output: { url, method } });
4242
} catch (error) {
43-
console.error("Error in fetch-post-task", { error });
43+
if (error instanceof SubtaskUnwrapError) {
44+
console.error("Error in fetch-post-task", {
45+
runId: error.runId,
46+
taskId: error.taskId,
47+
cause: error.cause,
48+
});
49+
}
4450
}
4551

4652
return {

0 commit comments

Comments
 (0)