Vercel Queues Node.js SDK
The @vercel/queue SDK provides a typed client for publishing, consuming, and managing messages with Vercel Queues.
pnpm i @vercel/queueCreate a QueueClient with a region. The client provides send, receive, handleCallback, and handleNodeCallback methods.
import { QueueClient } from '@vercel/queue';
const queue = new QueueClient({
region: process.env.QUEUE_REGION!,
});
export const { send, receive, handleCallback, handleNodeCallback } = queue;Use send to publish a message to a topic. The message can be any JSON-serializable value.
import { send } from '@/lib/queue';
export async function POST(request: Request) {
const body = await request.json();
const { messageId } = await send('orders', {
orderId: body.orderId,
action: 'process',
});
return Response.json({ messageId });
}await send('orders', payload, {
retentionSeconds: 3600,
delaySeconds: 60,
idempotencyKey: 'order-123',
});| Option | Type | Default | Description |
|---|---|---|---|
retentionSeconds | number | 24 hours | Message TTL. Minimum 60 seconds, maximum 24 hours (86,400 seconds) |
delaySeconds | number | Zero seconds | Delay before message becomes visible |
idempotencyKey | string | - | Deduplication key for the message |
Use handleCallback to create a push mode consumer for Next.js App Router routes. Messages are automatically acknowledged when your handler completes, and retried if it throws.
First, configure the consumer in vercel.json:
{
"functions": {
"app/api/queues/process-order/route.ts": {
"experimentalTriggers": [
{ "type": "queue/v2beta", "topic": "orders" }
]
}
}
}Then create the handler:
import { handleCallback } from '@/lib/queue';
export const POST = handleCallback(async (message, metadata) => {
await processOrder(message);
});The metadata object includes:
| Field | Type | Description |
|---|---|---|
messageId | string | Unique message identifier |
deliveryCount | number | Number of times this message has been delivered |
createdAt | Date | When the message was published |
expiresAt | Date | When the message expires |
topicName | string | Topic the message was published to |
consumerGroup | string | Consumer group receiving the message |
region | string | Region where the message is stored |
Control retry timing and handle poison messages with the retry option:
import { handleCallback } from '@/lib/queue';
export const POST = handleCallback(
async (message, metadata) => {
await processOrder(message);
},
{
retry: (error, metadata) => {
if (metadata.deliveryCount > 5) {
return { acknowledge: true };
}
const delay = Math.min(300, 2 ** metadata.deliveryCount * 5);
return { afterSeconds: delay };
},
},
);The retry callback can return:
| Return value | Behavior |
|---|---|
{ afterSeconds: number } | Retry after the specified delay |
{ acknowledge: true } | Acknowledge the message (stop retrying) |
undefined | Use default retry behavior |
Use receive to poll for messages. This is designed for consumers running outside of Vercel or for advanced on-Vercel setups.
import { QueueClient } from '@vercel/queue';
const { receive } = new QueueClient({ region: 'iad1' });
const result = await receive(
'orders',
'fulfillment',
async (message, metadata) => {
await processOrder(message);
},
);
if (!result.ok) {
console.log('No messages:', result.reason);
}await receive('orders', 'fulfillment', handler, {
limit: 10,
visibilityTimeoutSeconds: 300,
messageId: 'specific-message-id',
});| Option | Type | Default | Description |
|---|---|---|---|
limit | number | 1 | Maximum messages to receive (max: 10) |
visibilityTimeoutSeconds | number | 5 minutes | How long received messages are hidden from other consumers |
messageId | string | - | Receive a specific message by ID |
The SDK provides typed error classes for each failure mode:
import {
UnauthorizedError,
BadRequestError,
DuplicateMessageError,
MessageNotFoundError,
QueueEmptyError,
} from '@vercel/queue';
try {
await send('orders', payload);
} catch (error) {
if (error instanceof UnauthorizedError) {
// Invalid or expired token
} else if (error instanceof DuplicateMessageError) {
// Idempotency key collision
}
}The SDK supports multiple serialization formats through transports:
import { QueueClient, BufferTransport, StreamTransport } from '@vercel/queue';
const binaryQueue = new QueueClient({
region: process.env.QUEUE_REGION!,
transport: new BufferTransport(),
});
const streamQueue = new QueueClient({
region: process.env.QUEUE_REGION!,
transport: new StreamTransport(),
});| Transport | Description |
|---|---|
JsonTransport | Default. Serializes messages as JSON |
BufferTransport | Sends and receives raw binary data |
StreamTransport | Sends and receives ReadableStream for large payloads |
Was this helpful?