VercelVercel
Menu

Vercel Queues Node.js SDK

Last updated February 27, 2026

The @vercel/queue SDK provides a typed client for publishing, consuming, and managing messages with Vercel Queues.

Terminal
pnpm i @vercel/queue

Create a QueueClient with a region. The client provides send, receive, handleCallback, and handleNodeCallback methods.

lib/queue.ts
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.

app/api/orders/route.ts
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',
});
OptionTypeDefaultDescription
retentionSecondsnumber24 hoursMessage TTL. Minimum 60 seconds, maximum 24 hours (86,400 seconds)
delaySecondsnumberZero secondsDelay before message becomes visible
idempotencyKeystring-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:

vercel.json
{
  "functions": {
    "app/api/queues/process-order/route.ts": {
      "experimentalTriggers": [
        { "type": "queue/v2beta", "topic": "orders" }
      ]
    }
  }
}

Then create the handler:

app/api/queues/process-order/route.ts
import { handleCallback } from '@/lib/queue';
 
export const POST = handleCallback(async (message, metadata) => {
  await processOrder(message);
});

The metadata object includes:

FieldTypeDescription
messageIdstringUnique message identifier
deliveryCountnumberNumber of times this message has been delivered
createdAtDateWhen the message was published
expiresAtDateWhen the message expires
topicNamestringTopic the message was published to
consumerGroupstringConsumer group receiving the message
regionstringRegion where the message is stored

Control retry timing and handle poison messages with the retry option:

app/api/queues/process-order/route.ts
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 valueBehavior
{ afterSeconds: number }Retry after the specified delay
{ acknowledge: true }Acknowledge the message (stop retrying)
undefinedUse default retry behavior

Use receive to poll for messages. This is designed for consumers running outside of Vercel or for advanced on-Vercel setups.

lib/poll-worker.ts
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',
});
OptionTypeDefaultDescription
limitnumber1Maximum messages to receive (max: 10)
visibilityTimeoutSecondsnumber5 minutesHow long received messages are hidden from other consumers
messageIdstring-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(),
});
TransportDescription
JsonTransportDefault. Serializes messages as JSON
BufferTransportSends and receives raw binary data
StreamTransportSends and receives ReadableStream for large payloads

Was this helpful?

supported.