Skip to content

feat(vercel): queues#4127

Merged
pi0 merged 43 commits intomainfrom
feat/vercel-queues
Apr 13, 2026
Merged

feat(vercel): queues#4127
pi0 merged 43 commits intomainfrom
feat/vercel-queues

Conversation

@RihanArfan
Copy link
Copy Markdown
Member

@RihanArfan RihanArfan commented Mar 18, 2026

🔗 Linked issue

Related #1974

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Support Vercel Queues triggers through Nitro hooks, and documents using it with Nitro Tasks.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 18, 2026

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

Project Deployment Actions Updated (UTC)
nitro.build Ready Ready Preview, Comment Apr 13, 2026 10:20am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Vercel Queues support: docs and example app, Nitro types and a vercel:queue runtime hook, a runtime queue callback handler, build-time consumer registration in the Vercel preset, example project files, and updated tests for consumer generation and per-route function rules.

Changes

Cohort / File(s) Summary
Documentation
docs/2.deploy/20.providers/vercel.md, docs/4.examples/vercel-queues.md
New Queues docs and an end-to-end example: vercel.queues.triggers config, per-route function config notes (wildcards, array override semantics), sending messages from routes, and handling inbound queue callbacks via the vercel:queue hook and Nitro tasks.
Example project & scaffolding
examples/vercel-queues/package.json, examples/vercel-queues/README.md, examples/vercel-queues/tsconfig.json, examples/vercel-queues/vite.config.ts, examples/vercel-queues/nitro.config.ts
Added example project files, devDependency on @vercel/queue, Nitro config enabling experimental tasks, and supporting config/docs for the example.
Example runtime code
examples/vercel-queues/routes/send.ts, examples/vercel-queues/plugins/queue.ts, examples/vercel-queues/tasks/notifications/send.ts
Route handler to send queue messages with validation, a Nitro plugin registering a vercel:queue hook that dispatches matching messages to runTask, and a sample task definition.
Vercel preset: build & runtime
src/presets/vercel/preset.ts, src/presets/vercel/runtime/queue-handler.ts
Build-time registration of a lazy queue consumer route and augmentation of functionRules with experimentalTriggers; runtime handler forwards requests to @vercel/queue.handleCallback and invokes the vercel:queue hook.
Type definitions
src/presets/vercel/types.ts
Adds queues (handlerRoute, triggers) and functionRules to VercelOptions; declares the vercel:queue Nitro hook typings.
Preset utilities
src/presets/vercel/utils.ts
Identifier casing fix (hasfunctionRuleshasFunctionRules) and minor warning string typo fix.
Tests
test/presets/vercel.test.ts
Updated snapshots and assertions for per-route functionRules, added tests asserting creation/configuration of queue consumer function and corresponding route mapping.
Repo deps
package.json
Added @vercel/queue@^0.1.4 to devDependencies.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits format with type 'feat' and scope 'vercel', and accurately describes the main change of adding Vercel Queues support.
Description check ✅ Passed The description is related to the changeset, explaining that the PR adds Vercel Queues trigger support and documents integration with Nitro Tasks, linking to issue #1974.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/vercel-queues

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.

@RihanArfan RihanArfan marked this pull request as ready for review March 27, 2026 23:36
@RihanArfan RihanArfan requested a review from pi0 as a code owner March 27, 2026 23:36
Base automatically changed from feat/vercel-per-function-config to main March 27, 2026 23:55
Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (1)
src/presets/vercel/runtime/queue-handler.ts (1)

9-11: Remove the unnecessary type cast as Request.

The event.req object is already a Request instance in the Vercel runtime context (as evidenced by cron-handler.ts using event.req.headers.get() and event.req.waitUntil without casting). The type cast is redundant and should be removed for consistency with other handlers:

return handler(event.req);

instead of:

return handler(event.req as Request);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/presets/vercel/runtime/queue-handler.ts` around lines 9 - 11, The return
statement in the defineHandler wrapper unnecessarily casts event.req to Request;
update the defineHandler callback so it calls handler(event.req) without the
redundant "as Request" cast (locate the defineHandler(...) wrapper and the call
to handler in queue-handler.ts and remove the type assertion on event.req).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/2.deploy/20.providers/vercel.md`:
- Around line 58-86: The docs incorrectly reference vercel.routeFunctionConfig;
update all occurrences (text and the example in the nitro config) to use
vercel.functionRules so the sample matches the implementation/types;
specifically replace the symbol vercel.routeFunctionConfig with
vercel.functionRules in the example config block and any explanatory text, and
keep the rest of the example keys (e.g., maxDuration, memory, regions,
experimentalTriggers) unchanged.

In `@examples/vercel-queues/package.json`:
- Around line 3-12: The package.json scripts reference Vite ("dev": "vite dev",
"build": "vite build") but Vite is not listed in devDependencies; update the
devDependencies section to include "vite" (e.g., add a "vite": "latest" or
specific version) so the "dev" and "build" scripts resolve reliably alongside
existing entries like "nitro".

In `@src/presets/vercel/utils.ts`:
- Around line 64-69: Typo fix: change "accesible" to "accessible" in the warning
message. Update the string passed to nitro.logger.warn that references
`experimentalTriggers` on `vercel.functions` so the sentence reads "Routes with
queue triggers are not accessible on the web." Keep the rest of the message
intact and ensure references to `vercel.functionRules` remain unchanged.

---

Nitpick comments:
In `@src/presets/vercel/runtime/queue-handler.ts`:
- Around line 9-11: The return statement in the defineHandler wrapper
unnecessarily casts event.req to Request; update the defineHandler callback so
it calls handler(event.req) without the redundant "as Request" cast (locate the
defineHandler(...) wrapper and the call to handler in queue-handler.ts and
remove the type assertion on event.req).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 51894fd6-4700-46c8-8dc2-c7adc93647c3

📥 Commits

Reviewing files that changed from the base of the PR and between 589e8ad and 5fe65f0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • docs/2.deploy/20.providers/vercel.md
  • docs/4.examples/vercel-queues.md
  • examples/vercel-queues/README.md
  • examples/vercel-queues/nitro.config.ts
  • examples/vercel-queues/package.json
  • examples/vercel-queues/plugins/queue.ts
  • examples/vercel-queues/routes/send.ts
  • examples/vercel-queues/tasks/notifications/send.ts
  • examples/vercel-queues/tsconfig.json
  • examples/vercel-queues/vite.config.ts
  • package.json
  • src/presets/vercel/preset.ts
  • src/presets/vercel/runtime/queue-handler.ts
  • src/presets/vercel/types.ts
  • src/presets/vercel/utils.ts
  • test/fixture/nitro.config.ts
  • test/presets/vercel.test.ts

import { defineHandler } from "nitro";
import { useNitroHooks } from "nitro/app";

const handler = handleCallback(async (message, metadata) => {
Copy link
Copy Markdown
Member

@pi0 pi0 Mar 30, 2026

Choose a reason for hiding this comment

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

Checking pkg implementation, handleCallback itself returns a Promise, it will be at least a dangling promise! We should either move to each request or at least use defineLazyHander or avoid non handled promise

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

From claude:

import { handleCallback } from "@vercel/queue";
import { defineHandler } from "nitro";
import { useNitroHooks } from "nitro/app";
import { createClient } from "@vercel/queue"; // or however the client is constructed

const client = createClient({ token: process.env.QUEUE_TOKEN });

export default defineHandler(async (event) => {
  const request = event.req as Request;

  // Parse the callback request body that Vercel sends
  const body = await request.json(); // { queueName, consumerGroup, messageId, ... }

  await handleCallback(
    async (message, metadata) => {
      await useNitroHooks().callHook("vercel:queue", { message, metadata });
    },
    body,          // the parsed callback request
    { client }     // required options
  );

  return new Response("OK", { status: 200 });
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The @vercel/queue handleCallback returns a promise for a Response ((req) => Promise<Response>). Anything returned by Nitro gets passed up to it and then returned. Do we still need this?

We could move the handler inside the request handler.

A lazily-created default client auto-detects the region from VERCEL_REGION, falling back to iad1. https://vercel.com/docs/queues/sdk#top-level-exports

import { handleCallback } from "@vercel/queue";
import { defineHandler } from "nitro";
import { useNitroHooks } from "nitro/app";

export default defineHandler((event) => {
  return handleCallback(async (message, metadata) => {
    await useNitroHooks().callHook("vercel:queue", { message, metadata });
  })(event.req as Request);
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please check actual implementation in dist. From what i see it was immediately calling.


export default defineHandler((event) => {
return handleCallback(async (message, metadata) => {
await useNitroHooks().callHook("vercel:queue", { message, metadata });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you confirm how error handling currently works? We might wrap it to console.error + use nito.captureError (so other plugins can see errors) and if workflows has special handling for errors rethrow or otherwise do not.

Copy link
Copy Markdown
Member Author

@RihanArfan RihanArfan Apr 8, 2026

Choose a reason for hiding this comment

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

if your queue handler throws an exception or returns an HTTP 5xx status code (including 500), Vercel Queues treats this as a failure and will automatically schedule the message for a retry. The platform also retries on other error status codes such as 408 (Request Timeout), 409 (Conflict), and 429 (Too Many Requests), but not for status codes outside this set. Retries are not triggered for successful (2xx) responses or for other error codes like 400 (Bad Request)⁠.

We now console error, nitro.captureError and rethrow

Copy link
Copy Markdown
Contributor

@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.

Caution

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

⚠️ Outside diff range comments (1)
src/presets/vercel/utils.ts (1)

60-70: ⚠️ Potential issue | 🔴 Critical

Critical: Missing closing brace causes build failure.

The if block starting at line 60 is missing its closing } after the warning call. This causes all subsequent code (lines 70–189) to be incorrectly nested inside the if block, which in turn makes the export async function generateEdgeFunctionFiles at line 191 appear inside generateFunctionFiles—triggering the pipeline's SyntaxError: 'import', and 'export' cannot be used outside of module code.

🐛 Proposed fix
     nitro.logger.warn(
       "`experimentalTriggers` on the base `vercel.functions` config applies to the catch-all function and is likely not what you want. " +
         "Routes with queue triggers are not accessible on the web. " +
         "Use `vercel.functionRules` to attach triggers to specific routes instead."
     );
+  }

   const functionConfigPath = resolve(nitro.options.output.serverDir, ".vc-config.json");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/presets/vercel/utils.ts` around lines 60 - 70, The if block checking
Array.isArray(baseFunctionConfig.experimentalTriggers) is missing its closing
brace; insert a closing "}" immediately after the nitro.logger.warn(...) call so
the warning is the only statement inside that if, restoring correct scope for
subsequent declarations (e.g., functionConfigPath, generateFunctionFiles) and
ensuring generateEdgeFunctionFiles remains a top-level export.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/presets/vercel/utils.ts`:
- Around line 60-70: The if block checking
Array.isArray(baseFunctionConfig.experimentalTriggers) is missing its closing
brace; insert a closing "}" immediately after the nitro.logger.warn(...) call so
the warning is the only statement inside that if, restoring correct scope for
subsequent declarations (e.g., functionConfigPath, generateFunctionFiles) and
ensuring generateEdgeFunctionFiles remains a top-level export.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 78ac9397-0dc9-4b2a-b447-bad2a2dc6766

📥 Commits

Reviewing files that changed from the base of the PR and between 1707e9a and fb32548.

📒 Files selected for processing (1)
  • src/presets/vercel/utils.ts

@RihanArfan RihanArfan force-pushed the feat/vercel-queues branch from db5a896 to 20f3f60 Compare April 8, 2026 18:56
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 13, 2026

Open in StackBlitz

npm i https://pkg.pr.new/nitro@4127

commit: 29a4cc7

@pi0 pi0 merged commit 56bb444 into main Apr 13, 2026
12 checks passed
@pi0 pi0 deleted the feat/vercel-queues branch April 13, 2026 10:31
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.

2 participants