Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
da1808e
feat(vercel): allow overriding function config by route
RihanArfan Mar 17, 2026
24d92dc
fix: factor in base url with function route matching
RihanArfan Mar 18, 2026
ba05eb0
fix: replace arrays rather than merge
RihanArfan Mar 18, 2026
2758d9b
docs(vercel): function config overrides
RihanArfan Mar 18, 2026
af5775a
Update docs/2.deploy/20.providers/vercel.md
RihanArfan Mar 18, 2026
0b8d4c5
test(vercel): move to main fixture
RihanArfan Mar 18, 2026
a904f13
Merge remote-tracking branch 'origin/main' into feat/vercel-per-funct…
RihanArfan Mar 26, 2026
88c1972
Merge remote-tracking branch 'origin/main' into feat/vercel-per-funct…
RihanArfan Mar 27, 2026
ac313e1
feat(vercel): generate consumer for queue triggers
RihanArfan Mar 27, 2026
1dd21d5
fix(vercel): copy function output instead of symlinking when config o…
RihanArfan Mar 27, 2026
fba759c
test: update fixture
RihanArfan Mar 27, 2026
90d8bb6
test: copy function output instead of symlinking when config overridden
RihanArfan Mar 27, 2026
beb380e
test: copy function output instead of symlinking when config overridden
RihanArfan Mar 27, 2026
bbe5bf9
feat(vercel): queues
RihanArfan Mar 18, 2026
54892f1
refactor: use nitro routing
RihanArfan Mar 27, 2026
8c23741
style: move new utils to end
RihanArfan Mar 27, 2026
5355a5b
test: tidy snapshot
RihanArfan Mar 27, 2026
74e10ff
chore: add vercel queues example
RihanArfan Mar 27, 2026
0efd43b
chore: apply automated updates
autofix-ci[bot] Mar 27, 2026
b73f763
chore: apply automated updates (attempt 2/3)
autofix-ci[bot] Mar 27, 2026
c55c0e2
fix avoid writing extra fn
pi0 Mar 27, 2026
1823b2c
Merge branch 'feat/vercel-per-function-config' into feat/vercel-queues
RihanArfan Mar 28, 2026
5fe65f0
chore: rename to functionRules
RihanArfan Mar 28, 2026
1febc69
Merge remote-tracking branch 'origin/main' into feat/vercel-queues
RihanArfan Mar 30, 2026
ffba307
chore: add @vercel/queue
RihanArfan Mar 30, 2026
9fd1b1a
Delete examples/vercel-queues/.gitignore
RihanArfan Mar 30, 2026
e1ce4f1
chore: move to @vercel/queue dev deps
RihanArfan Mar 30, 2026
1f6cdf2
chore: apply automated updates
autofix-ci[bot] Mar 30, 2026
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
61 changes: 61 additions & 0 deletions docs/2.deploy/20.providers/vercel.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,67 @@ export default defineNitroConfig({

To prevent unauthorized access to the cron handler, set a `CRON_SECRET` environment variable in your Vercel project settings. When `CRON_SECRET` is set, Nitro validates the `Authorization` header on every cron invocation.

## Queues

:read-more{title="Vercel Queues" to="https://vercel.com/docs/queues"}

Nitro integrates with [Vercel Queues](https://vercel.com/docs/queues) to process messages asynchronously. Define your queue topics in the Nitro config and handle incoming messages with the `vercel:queue` runtime hook.

```ts [nitro.config.ts]
export default defineNitroConfig({
vercel: {
queues: {
triggers: [
{ topic: "orders" },
{ topic: "notifications" },
],
},
},
});
```

### Handling messages

Use the `vercel:queue` hook in a [Nitro plugin](/guide/plugins) to process incoming queue messages:

```ts [server/plugins/queues.ts]
export default defineNitroPlugin((nitro) => {
nitro.hooks.hook("vercel:queue", ({ message, metadata }) => {
console.log(`[${metadata.topicName}] Message ${metadata.messageId}:`, message);
});
});
```

### Running tasks from queue messages

You can use queue messages to trigger [Nitro tasks](/tasks):

```ts [server/plugins/queues.ts]
import { runTask } from "nitro/task";

export default defineNitroPlugin((nitro) => {
nitro.hooks.hook("vercel:queue", async ({ message, metadata }) => {
if (metadata.topicName === "orders") {
await runTask("orders:fulfill", { payload: message });
}
});
});
```

### Sending messages

Use the `@vercel/queue` package directly to send messages to a topic:

```ts [server/routes/api/orders.post.ts]
import { send } from "@vercel/queue";

export default defineEventHandler(async (event) => {
const order = await event.req.json();
const { messageId } = await send("orders", order);
return { messageId };
});
```

## Custom build output configuration

You can provide additional [build output configuration](https://vercel.com/docs/build-output-api/v3) using `vercel.config` key inside `nitro.config`. It will be merged with built-in auto-generated config.
Expand Down
192 changes: 192 additions & 0 deletions docs/4.examples/vercel-queues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
category: deploy
icon: i-simple-icons-vercel
---

# Vercel Queues

> Process background work asynchronously with Vercel Queues and Nitro tasks.
<!-- automd:ui-code-tree src="../../examples/vercel-queues" default="nitro.config.ts" ignore="README.md,GUIDE.md" expandAll -->

::code-tree{defaultValue="nitro.config.ts" expandAll}

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
serverDir: ".",
experimental: {
tasks: true,
},
vercel: {
queues: {
triggers: [{ topic: "notifications" }],
},
},
});
```

```json [package.json]
{
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build"
},
"devDependencies": {
"@vercel/queue": "^0.1.4",
"nitro": "latest"
}
}
```

```json [tsconfig.json]
{
"extends": "nitro/tsconfig"
}
```

```ts [vite.config.ts]
import { defineConfig } from "vite";
import { nitro } from "nitro/vite";

export default defineConfig({ plugins: [nitro()] });
```

```ts [plugins/queue.ts]
import { runTask } from "nitro/task";
import { definePlugin } from "nitro";

export default definePlugin((nitro) => {
nitro.hooks.hook("vercel:queue", async ({ message, metadata }) => {
if (metadata.topicName === "notifications") {
await runTask("notifications:send", {
payload: message as Record<string, unknown>,
});
}
});
});
```

```ts [routes/send.ts]
import { send } from "@vercel/queue";
import { defineHandler, HTTPError } from "nitro";

export default defineHandler(async (event) => {
const body = (await event.req.json()) as Record<string, unknown>;
if (!body.to || !body.subject || !body.body) {
throw new HTTPError({
status: 400,
message: "Missing required fields `to`, `subject` or `body`",
});
}

const { messageId } = await send("notifications", {
to: body.to,
subject: body.subject,
body: body.body,
});
return { messageId };
});
```

```ts [tasks/notifications/send.ts]
import { defineTask } from "nitro/task";

export default defineTask({
meta: {
description: "Send a notification",
},
async run({ payload }) {
console.log(`Sending notification to ${payload.to}: ${payload.subject}`);
return { result: "Notification sent" };
},
});
```

::

<!-- /automd -->

<!-- automd:file src="../../examples/vercel-queues/README.md" -->

Nitro integrates with [Vercel Queues](https://vercel.com/docs/queues) to process background work asynchronously.

## Configuration

Add queue triggers to your Nitro config. Nitro registers a queue consumer handler and makes incoming messages available via the `vercel:queue` runtime hook.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
experimental: {
tasks: true,
},
vercel: {
queues: {
triggers: [{ topic: "notifications" }],
},
},
});
```

## Sending Messages

Use the `@vercel/queue` SDK to send messages to a topic from any route.

```ts [routes/send.ts]
import { send } from "@vercel/queue";
import { defineHandler } from "nitro";

export default defineHandler(async (event) => {
const body = await event.req.json();
const { messageId } = await send("notifications", {
to: body.to,
subject: body.subject,
body: body.body,
});
return { messageId };
});
```

## Processing Messages

Listen for the `vercel:queue` hook in a plugin to handle incoming messages. This example dispatches them to a Nitro task.

```ts [plugins/queue.ts]
import { runTask } from "nitro/task";
import { definePlugin } from "nitro";

export default definePlugin((nitro) => {
nitro.hooks.hook("vercel:queue", async ({ message, metadata }) => {
if (metadata.topicName === "notifications") {
await runTask("notifications:send", {
payload: message as Record<string, unknown>,
});
}
});
});
```

```ts [tasks/notifications/send.ts]
import { defineTask } from "nitro/task";

export default defineTask({
meta: {
description: "Send a notification",
},
async run({ payload }) {
console.log(`Sending notification to ${payload.to}: ${payload.subject}`);
return { result: "Notification sent" };
},
});
```

<!-- /automd -->

## Learn More

- [Vercel Deployment](/deploy/providers/vercel)
- [Tasks](/docs/tasks)
72 changes: 72 additions & 0 deletions examples/vercel-queues/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
Nitro integrates with [Vercel Queues](https://vercel.com/docs/queues) to process background work asynchronously.

## Configuration

Add queue triggers to your Nitro config. Nitro registers a queue consumer handler and makes incoming messages available via the `vercel:queue` runtime hook.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
experimental: {
tasks: true,
},
vercel: {
queues: {
triggers: [{ topic: "notifications" }],
},
},
});
```

## Sending Messages

Use the `@vercel/queue` SDK to send messages to a topic from any route.

```ts [routes/send.ts]
import { send } from "@vercel/queue";
import { defineHandler } from "nitro";

export default defineHandler(async (event) => {
const body = await event.req.json();
const { messageId } = await send("notifications", {
to: body.to,
subject: body.subject,
body: body.body,
});
return { messageId };
});
```

## Processing Messages

Listen for the `vercel:queue` hook in a plugin to handle incoming messages. This example dispatches them to a Nitro task.

```ts [plugins/queue.ts]
import { runTask } from "nitro/task";
import { definePlugin } from "nitro";

export default definePlugin((nitro) => {
nitro.hooks.hook("vercel:queue", async ({ message, metadata }) => {
if (metadata.topicName === "notifications") {
await runTask("notifications:send", {
payload: message as Record<string, unknown>,
});
}
});
});
```

```ts [tasks/notifications/send.ts]
import { defineTask } from "nitro/task";

export default defineTask({
meta: {
description: "Send a notification",
},
async run({ payload }) {
console.log(`Sending notification to ${payload.to}: ${payload.subject}`);
return { result: "Notification sent" };
},
});
```
13 changes: 13 additions & 0 deletions examples/vercel-queues/nitro.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig } from "nitro";

export default defineConfig({
serverDir: ".",
experimental: {
tasks: true,
},
vercel: {
queues: {
triggers: [{ topic: "notifications" }],
},
},
});
11 changes: 11 additions & 0 deletions examples/vercel-queues/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build"
},
"devDependencies": {
"@vercel/queue": "^0.1.4",
"nitro": "latest"
}
}
12 changes: 12 additions & 0 deletions examples/vercel-queues/plugins/queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { runTask } from "nitro/task";
import { definePlugin } from "nitro";

export default definePlugin((nitro) => {
nitro.hooks.hook("vercel:queue", async ({ message, metadata }) => {
if (metadata.topicName === "notifications") {
await runTask("notifications:send", {
payload: message as Record<string, unknown>,
});
}
});
});
19 changes: 19 additions & 0 deletions examples/vercel-queues/routes/send.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { send } from "@vercel/queue";
import { defineHandler, HTTPError } from "nitro";

export default defineHandler(async (event) => {
const body = (await event.req.json()) as Record<string, unknown>;
if (!body.to || !body.subject || !body.body) {
throw new HTTPError({
status: 400,
message: "Missing required fields `to`, `subject` or `body`",
});
}

const { messageId } = await send("notifications", {
to: body.to,
subject: body.subject,
body: body.body,
});
return { messageId };
});
Loading
Loading