Skip to content

feat: add list mode to GET /api/tasks/runs#245

Merged
sweetmantech merged 10 commits intotestfrom
sweetmantech/myc-4347-api-get-apitasksruns-make-runid-optional-if-no-runid-search
Feb 26, 2026
Merged

feat: add list mode to GET /api/tasks/runs#245
sweetmantech merged 10 commits intotestfrom
sweetmantech/myc-4347-api-get-apitasksruns-make-runid-optional-if-no-runid-search

Conversation

@sweetmantech
Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech commented Feb 25, 2026

Summary

  • Make runId optional in GET /api/tasks/runs — when omitted, returns recent runs for the authenticated account
  • Query Trigger.dev runs.list() with account:<accountId> tag filter
  • Add optional limit query parameter (default 20, max 100)
  • Existing retrieve-by-runId behavior is unchanged
  • 16 tests passing (11 handler tests + 5 listTaskRuns tests)

Test plan

  • curl /api/tasks/runs (no runId, with auth) returns { status: "success", runs: [...] }
  • curl /api/tasks/runs?runId=<id> returns existing single-run behavior unchanged
  • curl /api/tasks/runs?limit=5 returns at most 5 runs
  • All tests pass: pnpm test lib/tasks/__tests__/getTaskRunHandler.test.ts lib/trigger/__tests__/listTaskRuns.test.ts

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added support for listing recent task runs with a configurable limit (1–100, default 20).
    • Task run endpoint now returns runs as an array for both list and single-run requests, and supports fetching either an individual run or multiple recent runs.

Make runId optional. When omitted, returns recent runs for the
authenticated account by querying Trigger.dev runs tagged with
account:<accountId>. Add optional limit parameter (default 20, max 100).

- Update validateGetTaskRunQuery to return discriminated union
- Add listTaskRuns function using runs.list with tag filter
- Branch handler logic on retrieve vs list mode
- Add tests for list mode (16 total tests passing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-api Ready Ready Preview Feb 26, 2026 2:48am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 25, 2026

Warning

Rate limit exceeded

@sweetmantech has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c79564c and dd37d44.

⛔ Files ignored due to path filters (3)
  • lib/tasks/__tests__/getTaskRunHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/trigger/__tests__/listTaskRuns.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/trigger/__tests__/retrieveTaskRun.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (3)
  • lib/tasks/getTaskRunHandler.ts
  • lib/trigger/listTaskRuns.ts
  • lib/trigger/retrieveTaskRun.ts
📝 Walkthrough

Walkthrough

Adds a list mode to the task-run endpoint, introduces listTaskRuns and a date utility, refactors retrieveTaskRun to return a uniform TaskRun shape, and updates query validation to a discriminated union supporting "retrieve" and "list" modes.

Changes

Cohort / File(s) Summary
Query Validation
lib/tasks/validateGetTaskRunQuery.ts
Introduces discriminated union return (`mode: "retrieve"
Handler
lib/tasks/getTaskRunHandler.ts
Imports listTaskRuns, logs after validation, branches on query mode: calls listTaskRuns(accountId, limit) for "list" and returns { status: "success", runs }; for "retrieve" wraps single run in runs: [result]. Preserves existing error responses and CORS headers.
Trigger — Listing
lib/trigger/listTaskRuns.ts
New function listTaskRuns(accountId: string, limit?: number): Promise<TaskRun[]> that queries Trigger.dev runs by account:${accountId} tag, maps results to TaskRun-shaped objects, normalizes created/started/finished timestamps, and logs inputs/results.
Trigger — Retrieval / Types
lib/trigger/retrieveTaskRun.ts
Replaces previous union result types with a single exported TaskRun interface and returns `TaskRun
Trigger — Date Utility
lib/trigger/toISOStringOrNull.ts
Adds `toISOStringOrNull(value: Date

Sequence Diagram

sequenceDiagram
    participant Client
    participant Handler as API Handler
    participant Validator as Query Validator
    participant ListService as listTaskRuns
    participant TriggerAPI as Trigger.dev API
    participant Retriever as retrieveTaskRun

    Client->>Handler: GET /task-runs?[runId | ?limit]
    Handler->>Validator: Validate query params

    alt List Mode (no runId)
        Validator-->>Handler: { mode: "list", accountId, limit }
        Handler->>ListService: listTaskRuns(accountId, limit)
        ListService->>TriggerAPI: runs.list(tag=account:accountId, limit)
        TriggerAPI-->>ListService: Raw task runs
        ListService-->>ListService: Normalize dates (toISOStringOrNull) & map to TaskRun
        ListService-->>Handler: TaskRun[]
        Handler-->>Client: { status: "success", runs: [...] } (200 + CORS)
    else Retrieve Mode (with runId)
        Validator-->>Handler: { mode: "retrieve", runId }
        Handler->>Retriever: retrieveTaskRun(runId)
        Retriever->>TriggerAPI: runs.get(runId)
        TriggerAPI-->>Retriever: Raw task run
        Retriever-->>Handler: TaskRun | null
        Handler-->>Client: { status: "success", runs: [TaskRun] } (200) or 404/500
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

✨ A query splits — retrieve or list in play,
Runs gather like stars from Trigger.dev's display,
Dates turn to ISO, types now sing in tune,
One uniform shape beneath the code's bright moon,
Solid, small changes — the endpoint grew today.

🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Solid & Clean Code ✅ Passed PR demonstrates excellent adherence to SOLID principles and clean code practices with single-responsibility functions, discriminated union patterns, DRY utilities, clear naming, and strong type safety throughout.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sweetmantech/myc-4347-api-get-apitasksruns-make-runid-optional-if-no-runid-search

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

listTaskRuns now maps Trigger.dev statuses to pending/complete/failed
matching the same shape as retrieveTaskRun, with an added id field.
Removes the separate TaskRunListItem type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/tasks/getTaskRunHandler.ts (1)

51-51: ⚠️ Potential issue | 🟡 Minor

Update catch log text to match both retrieve and list flows.

Line 51 says “retrieving” even when list mode fails, which makes ops debugging noisier.

🔧 Suggested fix
-    console.error("Error retrieving task run:", error);
+    console.error("Error handling task run request:", error);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/getTaskRunHandler.ts` at line 51, The catch log in
getTaskRunHandler.ts currently uses "Error retrieving task run:" which is
misleading for the list flow; update the error message logged in the catch block
(where console.error("Error retrieving task run:", error) is called) to a
neutral message that covers both operations—e.g., "Error retrieving or listing
task run(s):"—so both the retrieve and list flows produce an accurate, unified
log entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/tasks/validateGetTaskRunQuery.ts`:
- Around line 8-12: The runId schema in validateGetTaskRunQuery.ts trims after
length validation, allowing whitespace-only values to pass .min(1) then become
empty and trigger list-mode; change the schema so trimming happens before length
checks (e.g., apply .transform(val => val.trim()) or .trim() first, then .min(1)
or use a .refine on the trimmed value) to ensure whitespace-only runId returns a
400, and apply the same ordering/fix to the similar schema block referenced
around lines 63-67 to validate trimmed input before checking length.
- Around line 7-23: Export the Zod schema and add an inferred TypeScript type
from it: make getTaskRunQuerySchema exported (export const getTaskRunQuerySchema
= ...) and add an exported inferred type using z.infer (e.g., export type
InferredGetTaskRunQuery = z.infer<typeof getTaskRunQuerySchema>), leaving the
existing ValidatedRetrieveQuery, ValidatedListQuery and GetTaskRunQuery types
intact so callers can use either the raw schema type or the existing validated
union types.

In `@lib/trigger/listTaskRuns.ts`:
- Line 43: Replace the unsafe type assertion for metadata with a runtime guard
in listTaskRuns: instead of (run.metadata as Record<string, unknown>) ?? null,
check that run.metadata is a non-null plain object (typeof run.metadata ===
'object' && run.metadata !== null && !Array.isArray(run.metadata)) and only then
assign it to the metadata property; otherwise set metadata to null. Update the
metadata assignment in the object returned by the function (the place
referencing run.metadata) so the response shape cannot receive
strings/numbers/arrays.

---

Outside diff comments:
In `@lib/tasks/getTaskRunHandler.ts`:
- Line 51: The catch log in getTaskRunHandler.ts currently uses "Error
retrieving task run:" which is misleading for the list flow; update the error
message logged in the catch block (where console.error("Error retrieving task
run:", error) is called) to a neutral message that covers both operations—e.g.,
"Error retrieving or listing task run(s):"—so both the retrieve and list flows
produce an accurate, unified log entry.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5430d5 and 509e357.

⛔ Files ignored due to path filters (2)
  • lib/tasks/__tests__/getTaskRunHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/trigger/__tests__/listTaskRuns.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (3)
  • lib/tasks/getTaskRunHandler.ts
  • lib/tasks/validateGetTaskRunQuery.ts
  • lib/trigger/listTaskRuns.ts

Comment on lines 7 to +23
const getTaskRunQuerySchema = z.object({
runId: z
.string({ message: "runId is required" })
.min(1, "runId is required")
.transform(val => val.trim()),
.string()
.min(1)
.transform(val => val.trim())
.optional(),
limit: z.coerce
.number()
.int()
.min(1)
.max(100)
.default(20),
});

export type GetTaskRunQuery = z.infer<typeof getTaskRunQuerySchema>;
export type ValidatedRetrieveQuery = { mode: "retrieve"; runId: string };
export type ValidatedListQuery = { mode: "list"; accountId: string; limit: number };
export type GetTaskRunQuery = ValidatedRetrieveQuery | ValidatedListQuery;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Export the validation schema and an inferred schema type from this validate module.

This file currently exports validated union types, but not the schema itself nor an inferred schema type.

♻️ Suggested change
-const getTaskRunQuerySchema = z.object({
+export const getTaskRunQuerySchema = z.object({
   ...
 });
+
+export type GetTaskRunQuerySchema = z.infer<typeof getTaskRunQuerySchema>;

As per coding guidelines: lib/**/validate*.ts: “Create validate functions ... files that export both the schema and inferred TypeScript type”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/validateGetTaskRunQuery.ts` around lines 7 - 23, Export the Zod
schema and add an inferred TypeScript type from it: make getTaskRunQuerySchema
exported (export const getTaskRunQuerySchema = ...) and add an exported inferred
type using z.infer (e.g., export type InferredGetTaskRunQuery = z.infer<typeof
getTaskRunQuerySchema>), leaving the existing ValidatedRetrieveQuery,
ValidatedListQuery and GetTaskRunQuery types intact so callers can use either
the raw schema type or the existing validated union types.

Comment on lines 8 to +12
runId: z
.string({ message: "runId is required" })
.min(1, "runId is required")
.transform(val => val.trim()),
.string()
.min(1)
.transform(val => val.trim())
.optional(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Trim runId before length validation to prevent accidental list-mode fallback.

At Line 10-11, whitespace-only runId (e.g. " ") passes .min(1) and is then transformed to ""; at Line 63 this falls through to list mode. That should be a 400, not a mode switch.

🔧 Suggested fix
 const getTaskRunQuerySchema = z.object({
   runId: z
     .string()
-    .min(1)
-    .transform(val => val.trim())
+    .trim()
+    .min(1)
     .optional(),
@@
-  if (result.data.runId) {
+  if (result.data.runId !== undefined) {
     return { mode: "retrieve", runId: result.data.runId };
   }

Also applies to: 63-67

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/validateGetTaskRunQuery.ts` around lines 8 - 12, The runId schema
in validateGetTaskRunQuery.ts trims after length validation, allowing
whitespace-only values to pass .min(1) then become empty and trigger list-mode;
change the schema so trimming happens before length checks (e.g., apply
.transform(val => val.trim()) or .trim() first, then .min(1) or use a .refine on
the trimmed value) to ensure whitespace-only runId returns a 400, and apply the
same ordering/fix to the similar schema block referenced around lines 63-67 to
validate trimmed input before checking length.

Comment thread lib/trigger/listTaskRuns.ts Outdated
return result.data.map(run => {
const common = {
id: run.id,
metadata: (run.metadata as Record<string, unknown>) ?? null,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard metadata at runtime instead of relying on a type assertion.

At Line 43, the cast can pass through non-object values (e.g., string/number) and break the declared response shape.

🔧 Suggested fix
-      metadata: (run.metadata as Record<string, unknown>) ?? null,
+      metadata:
+        run.metadata && typeof run.metadata === "object" && !Array.isArray(run.metadata)
+          ? (run.metadata as Record<string, unknown>)
+          : null,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
metadata: (run.metadata as Record<string, unknown>) ?? null,
metadata:
run.metadata && typeof run.metadata === "object" && !Array.isArray(run.metadata)
? (run.metadata as Record<string, unknown>)
: null,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/trigger/listTaskRuns.ts` at line 43, Replace the unsafe type assertion
for metadata with a runtime guard in listTaskRuns: instead of (run.metadata as
Record<string, unknown>) ?? null, check that run.metadata is a non-null plain
object (typeof run.metadata === 'object' && run.metadata !== null &&
!Array.isArray(run.metadata)) and only then assign it to the metadata property;
otherwise set metadata to null. Update the metadata assignment in the object
returned by the function (the place referencing run.metadata) so the response
shape cannot receive strings/numbers/arrays.

Both retrieve and list modes now return the same response shape:
{ status: "success", runs: TaskRunResult[] }. When runId is provided,
runs is a single-element array. This eliminates the oneOf in the
response schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add id field to retrieveTaskRun return type
- Update validateGetTaskRunQuery tests for mode-based return type
- Treat empty runId as list mode (not validation error)
- Update retrieveTaskRun tests with id in expectedCommon

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Trigger.dev runs.list() API does not include output on list items
(only runs.retrieve() does). Set data to null for completed runs in
list mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sweetmantech and others added 2 commits February 25, 2026 21:15
SRP — move shared date conversion utility out of listTaskRuns and
use it in both listTaskRuns and retrieveTaskRun.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Log the tag query, result count, and resolved accountId to debug
empty array responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Return raw status strings (COMPLETED, FAILED, EXECUTING, etc.)
from retrieveTaskRun and listTaskRuns instead of mapping to
pending/complete/failed. This preserves all SDK status granularity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
lib/tasks/validateGetTaskRunQuery.ts (1)

7-23: ⚠️ Potential issue | 🟠 Major

Export the validation schema and an inferred schema type from this module.

getTaskRunQuerySchema is still private, and there’s no exported z.infer type from it. That makes schema reuse and type alignment harder in validate consumers/tests.

♻️ Suggested change
-const getTaskRunQuerySchema = z.object({
+export const getTaskRunQuerySchema = z.object({
   runId: z
     .string()
     .min(1)
     .transform(val => val.trim())
     .optional(),
   limit: z.coerce
     .number()
     .int()
     .min(1)
     .max(100)
     .default(20),
 });
 
+export type GetTaskRunQuerySchema = z.infer<typeof getTaskRunQuerySchema>;
 export type ValidatedRetrieveQuery = { mode: "retrieve"; runId: string };
 export type ValidatedListQuery = { mode: "list"; accountId: string; limit: number };
 export type GetTaskRunQuery = ValidatedRetrieveQuery | ValidatedListQuery;

As per coding guidelines: lib/**/validate*.ts files must “export both the schema and inferred TypeScript type.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/validateGetTaskRunQuery.ts` around lines 7 - 23, Export the
validation schema and its inferred type so other modules/tests can reuse them:
make getTaskRunQuerySchema exported (export const getTaskRunQuerySchema) and add
an exported inferred type e.g. export type GetTaskRunQuerySchema =
z.infer<typeof getTaskRunQuerySchema>; keep existing explicit union types
(ValidatedRetrieveQuery, ValidatedListQuery, GetTaskRunQuery) as-is for
compatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@lib/tasks/validateGetTaskRunQuery.ts`:
- Around line 7-23: Export the validation schema and its inferred type so other
modules/tests can reuse them: make getTaskRunQuerySchema exported (export const
getTaskRunQuerySchema) and add an exported inferred type e.g. export type
GetTaskRunQuerySchema = z.infer<typeof getTaskRunQuerySchema>; keep existing
explicit union types (ValidatedRetrieveQuery, ValidatedListQuery,
GetTaskRunQuery) as-is for compatibility.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 509e357 and c79564c.

⛔ Files ignored due to path filters (4)
  • lib/tasks/__tests__/getTaskRunHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/tasks/__tests__/validateGetTaskRunQuery.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/trigger/__tests__/listTaskRuns.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/trigger/__tests__/retrieveTaskRun.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (5)
  • lib/tasks/getTaskRunHandler.ts
  • lib/tasks/validateGetTaskRunQuery.ts
  • lib/trigger/listTaskRuns.ts
  • lib/trigger/retrieveTaskRun.ts
  • lib/trigger/toISOStringOrNull.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/trigger/listTaskRuns.ts
  • lib/tasks/getTaskRunHandler.ts

Return Trigger.dev SDK objects directly from retrieveTaskRun and
listTaskRuns instead of manually mapping fields. Removes the
TaskRun interface, toISOStringOrNull utility, and all unnecessary
transformation code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant