@vercel/queue 0.0.0-alpha.36 → 0.0.0-alpha.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +212 -182
- package/dist/{types-C7IKe67P.d.mts → callback-lq_sorrn.d.mts} +249 -40
- package/dist/{types-C7IKe67P.d.ts → callback-lq_sorrn.d.ts} +249 -40
- package/dist/index.d.mts +74 -338
- package/dist/index.d.ts +74 -338
- package/dist/index.js +904 -930
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +900 -928
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs-pages.d.mts +47 -27
- package/dist/nextjs-pages.d.ts +47 -27
- package/dist/nextjs-pages.js +322 -373
- package/dist/nextjs-pages.js.map +1 -1
- package/dist/nextjs-pages.mjs +322 -373
- package/dist/nextjs-pages.mjs.map +1 -1
- package/dist/web.d.mts +60 -0
- package/dist/web.d.ts +60 -0
- package/dist/web.js +1457 -0
- package/dist/web.js.map +1 -0
- package/dist/web.mjs +1420 -0
- package/dist/web.mjs.map +1 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -5,12 +5,13 @@ A TypeScript client library for interacting with the Vercel Queue Service API, d
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Automatic Queue Triggering**: Vercel automatically triggers your API routes when messages are ready
|
|
8
|
-
- **Next.js Integration**: Built-in support for Next.js
|
|
8
|
+
- **Next.js Integration**: Built-in support for Next.js App Router and Pages Router
|
|
9
9
|
- **Generic Payload Support**: Send and receive any type of data with type safety
|
|
10
10
|
- **Pub/Sub Pattern**: Topic-based messaging with consumer groups
|
|
11
11
|
- **Type Safety**: Full TypeScript support with generic types
|
|
12
12
|
- **Streaming Support**: Handle large payloads efficiently
|
|
13
13
|
- **Customizable Serialization**: Use built-in transports (JSON, Buffer, Stream) or create your own
|
|
14
|
+
- **Framework Adapters**: Web API, Next.js App Router, and Pages Router support
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -39,6 +40,8 @@ vc env pull
|
|
|
39
40
|
|
|
40
41
|
The library reads your `vercel.json` configuration, discovers your queue handlers, and triggers them automatically when messages are sent.
|
|
41
42
|
|
|
43
|
+
> **Note:** Local dev mode is enabled when `NODE_ENV=development`. Most frameworks (Next.js, etc.) set this automatically when running `npm run dev`.
|
|
44
|
+
|
|
42
45
|
### Example Workflow
|
|
43
46
|
|
|
44
47
|
```bash
|
|
@@ -48,18 +51,6 @@ npm run dev
|
|
|
48
51
|
# Send messages - they process locally automatically!
|
|
49
52
|
```
|
|
50
53
|
|
|
51
|
-
### TypeScript Configuration
|
|
52
|
-
|
|
53
|
-
Update your `tsconfig.json` to use `"bundler"` module resolution for proper package export resolution:
|
|
54
|
-
|
|
55
|
-
```json
|
|
56
|
-
{
|
|
57
|
-
"compilerOptions": {
|
|
58
|
-
"moduleResolution": "bundler"
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
54
|
### Publishing Messages
|
|
64
55
|
|
|
65
56
|
The `send` function can be used anywhere in your codebase to publish messages to a queue:
|
|
@@ -109,124 +100,108 @@ Messages are consumed using API routes that Vercel automatically triggers when m
|
|
|
109
100
|
|
|
110
101
|
#### 1. Create API Routes
|
|
111
102
|
|
|
112
|
-
#####
|
|
103
|
+
##### Web API (`@vercel/queue/web`)
|
|
113
104
|
|
|
114
|
-
The
|
|
105
|
+
The `handleCallback` from `@vercel/queue/web` returns a standard `(Request) => Promise<Response>` handler. It works with any framework that uses the Web API `Request`/`Response` types, including Next.js App Router, Hono, and others.
|
|
106
|
+
|
|
107
|
+
**Next.js App Router:**
|
|
115
108
|
|
|
116
109
|
```typescript
|
|
117
|
-
// app/api/queue/route.ts
|
|
118
|
-
import { handleCallback } from "@vercel/queue";
|
|
119
|
-
|
|
120
|
-
export const POST = handleCallback({
|
|
121
|
-
// Single topic with one consumer
|
|
122
|
-
"my-topic": {
|
|
123
|
-
"my-consumer": async (message, metadata) => {
|
|
124
|
-
// metadata includes: { messageId, deliveryCount, createdAt, topicName, consumerGroup }
|
|
125
|
-
console.log("Processing message:", message);
|
|
126
|
-
|
|
127
|
-
// If this throws an error, the message will be automatically retried
|
|
128
|
-
await processMessage(message);
|
|
129
|
-
},
|
|
130
|
-
},
|
|
110
|
+
// app/api/queue/my-topic/route.ts
|
|
111
|
+
import { handleCallback } from "@vercel/queue/web";
|
|
131
112
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
await trackOrder(order);
|
|
139
|
-
},
|
|
140
|
-
},
|
|
113
|
+
export const POST = handleCallback(async (message, metadata) => {
|
|
114
|
+
// metadata includes: { messageId, deliveryCount, createdAt, topicName, consumerGroup }
|
|
115
|
+
console.log("Processing message:", message);
|
|
116
|
+
|
|
117
|
+
// If this throws an error, the message will be automatically retried
|
|
118
|
+
await processMessage(message);
|
|
141
119
|
});
|
|
142
120
|
```
|
|
143
121
|
|
|
144
|
-
|
|
122
|
+
**Hono:**
|
|
145
123
|
|
|
146
|
-
|
|
124
|
+
```typescript
|
|
125
|
+
import { Hono } from "hono";
|
|
126
|
+
import { handleCallback } from "@vercel/queue/web";
|
|
147
127
|
|
|
148
|
-
|
|
128
|
+
const app = new Hono();
|
|
129
|
+
|
|
130
|
+
app.post(
|
|
131
|
+
"/api/queue",
|
|
132
|
+
handleCallback(async (message, metadata) => {
|
|
133
|
+
console.log("Processing:", message);
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
export default app;
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For multiple topics/consumers, create separate route files:
|
|
149
141
|
|
|
150
142
|
```typescript
|
|
151
|
-
//
|
|
152
|
-
import { handleCallback } from "@vercel/queue/
|
|
143
|
+
// app/api/queue/orders/fulfillment/route.ts
|
|
144
|
+
import { handleCallback } from "@vercel/queue/web";
|
|
153
145
|
|
|
154
|
-
export
|
|
155
|
-
|
|
156
|
-
"my-consumer": async (message, metadata) => {
|
|
157
|
-
console.log("Processing message:", message);
|
|
158
|
-
await processMessage(message);
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
"order-events": {
|
|
162
|
-
fulfillment: async (order, metadata) => {
|
|
163
|
-
await processOrder(order);
|
|
164
|
-
},
|
|
165
|
-
analytics: async (order, metadata) => {
|
|
166
|
-
await trackOrder(order);
|
|
167
|
-
},
|
|
168
|
-
},
|
|
146
|
+
export const POST = handleCallback(async (order, metadata) => {
|
|
147
|
+
await processOrder(order);
|
|
169
148
|
});
|
|
170
149
|
```
|
|
171
150
|
|
|
172
|
-
|
|
151
|
+
```typescript
|
|
152
|
+
// app/api/queue/orders/analytics/route.ts
|
|
153
|
+
import { handleCallback } from "@vercel/queue/web";
|
|
173
154
|
|
|
174
|
-
|
|
155
|
+
export const POST = handleCallback(async (order, metadata) => {
|
|
156
|
+
await trackOrder(order);
|
|
157
|
+
});
|
|
158
|
+
```
|
|
175
159
|
|
|
176
|
-
|
|
160
|
+
##### Pages Router (`@vercel/queue/nextjs/pages`)
|
|
177
161
|
|
|
178
|
-
|
|
162
|
+
For Next.js Pages Router, import from `@vercel/queue/nextjs/pages`. This returns a `(req, res) => Promise<void>` handler:
|
|
179
163
|
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
"consumer": "my-consumer",
|
|
189
|
-
"retryAfterSeconds": 60,
|
|
190
|
-
"initialDelaySeconds": 0
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
"type": "queue/v1beta",
|
|
194
|
-
"topic": "order-events",
|
|
195
|
-
"consumer": "fulfillment"
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
"type": "queue/v1beta",
|
|
199
|
-
"topic": "order-events",
|
|
200
|
-
"consumer": "analytics",
|
|
201
|
-
"retryAfterSeconds": 300
|
|
202
|
-
}
|
|
203
|
-
]
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
164
|
+
```typescript
|
|
165
|
+
// pages/api/queue/my-topic.ts
|
|
166
|
+
import { handleCallback } from "@vercel/queue/nextjs/pages";
|
|
167
|
+
|
|
168
|
+
export default handleCallback(async (message, metadata) => {
|
|
169
|
+
console.log("Processing message:", message);
|
|
170
|
+
await processMessage(message);
|
|
171
|
+
});
|
|
207
172
|
```
|
|
208
173
|
|
|
209
|
-
|
|
174
|
+
#### 2. Configure vercel.json
|
|
175
|
+
|
|
176
|
+
Configure which topics and consumers your API routes handle.
|
|
210
177
|
|
|
211
178
|
```json
|
|
212
179
|
{
|
|
213
180
|
"functions": {
|
|
214
|
-
"
|
|
181
|
+
"app/api/queue/my-topic/route.ts": {
|
|
215
182
|
"experimentalTriggers": [
|
|
216
183
|
{
|
|
217
|
-
"type": "queue/
|
|
184
|
+
"type": "queue/v2beta",
|
|
218
185
|
"topic": "my-topic",
|
|
219
186
|
"consumer": "my-consumer",
|
|
220
187
|
"retryAfterSeconds": 60,
|
|
221
188
|
"initialDelaySeconds": 0
|
|
222
|
-
}
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
"app/api/queue/orders/fulfillment/route.ts": {
|
|
193
|
+
"experimentalTriggers": [
|
|
223
194
|
{
|
|
224
|
-
"type": "queue/
|
|
195
|
+
"type": "queue/v2beta",
|
|
225
196
|
"topic": "order-events",
|
|
226
197
|
"consumer": "fulfillment"
|
|
227
|
-
}
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
},
|
|
201
|
+
"app/api/queue/orders/analytics/route.ts": {
|
|
202
|
+
"experimentalTriggers": [
|
|
228
203
|
{
|
|
229
|
-
"type": "queue/
|
|
204
|
+
"type": "queue/v2beta",
|
|
230
205
|
"topic": "order-events",
|
|
231
206
|
"consumer": "analytics",
|
|
232
207
|
"retryAfterSeconds": 300
|
|
@@ -246,47 +221,48 @@ Configure which topics and consumers your API route handles.
|
|
|
246
221
|
- **Automatic Triggering**: Vercel triggers your API routes when messages are available
|
|
247
222
|
- **Message Processing**: Your API routes receive message metadata via headers
|
|
248
223
|
- **Configuration**: The `vercel.json` file tells Vercel which routes handle which topics/consumers
|
|
224
|
+
- **Delivery Modes**: The server uses CloudEvents binary content mode to deliver messages. For small messages, the full payload and receipt handle are pushed directly in the HTTP body and headers, avoiding an extra API fetch. For large messages, only the message ID is sent and the SDK fetches the payload.
|
|
249
225
|
|
|
250
226
|
## Advanced Features
|
|
251
227
|
|
|
252
|
-
### Client
|
|
228
|
+
### Custom Client Configuration
|
|
253
229
|
|
|
254
|
-
For custom configuration (tokens, headers,
|
|
230
|
+
For custom configuration (tokens, headers, transport), create a `QueueClient` and pass it via options:
|
|
255
231
|
|
|
256
232
|
```typescript
|
|
257
|
-
import {
|
|
233
|
+
import { QueueClient, send } from "@vercel/queue";
|
|
234
|
+
import { handleCallback } from "@vercel/queue/web";
|
|
258
235
|
|
|
259
|
-
const client = new
|
|
260
|
-
token: "my-token",
|
|
261
|
-
headers: { "X-Custom": "header" },
|
|
262
|
-
pinToDeployment: false, // Optional: disable deployment pinning (default: true)
|
|
236
|
+
const client = new QueueClient({
|
|
237
|
+
token: "my-token",
|
|
238
|
+
headers: { "X-Custom": "header" },
|
|
263
239
|
});
|
|
264
240
|
|
|
265
|
-
// Send
|
|
266
|
-
await
|
|
241
|
+
// Send with custom client
|
|
242
|
+
await send("my-topic", { hello: "world" }, { client });
|
|
267
243
|
|
|
268
|
-
// Handle callbacks
|
|
269
|
-
export const POST =
|
|
270
|
-
|
|
271
|
-
"my-group": async (msg, meta) => console.log(msg),
|
|
272
|
-
},
|
|
244
|
+
// Handle callbacks with custom client
|
|
245
|
+
export const POST = handleCallback(async (msg, meta) => console.log(msg), {
|
|
246
|
+
client,
|
|
273
247
|
});
|
|
274
248
|
```
|
|
275
249
|
|
|
276
|
-
###
|
|
250
|
+
### Core Handler (Framework Agnostic)
|
|
277
251
|
|
|
278
|
-
For custom
|
|
252
|
+
For custom framework integration, use the core `handleCallback` from `@vercel/queue`. It takes parsed request data and throws on errors:
|
|
279
253
|
|
|
280
254
|
```typescript
|
|
281
|
-
import {
|
|
282
|
-
|
|
283
|
-
export async function POST(request: Request) {
|
|
284
|
-
const { queueName, consumerGroup, messageId } = await parseCallback(request);
|
|
285
|
-
|
|
286
|
-
// Use the parsed information for custom processing...
|
|
287
|
-
await myWorkflow.handleWebhook(queueName, consumerGroup, messageId);
|
|
255
|
+
import { handleCallback, parseRawCallback } from "@vercel/queue";
|
|
288
256
|
|
|
289
|
-
|
|
257
|
+
// In your framework handler:
|
|
258
|
+
const parsed = parseRawCallback(body, headers);
|
|
259
|
+
try {
|
|
260
|
+
await handleCallback(async (msg, meta) => {
|
|
261
|
+
console.log("Processing:", msg);
|
|
262
|
+
}, parsed);
|
|
263
|
+
// success
|
|
264
|
+
} catch (error) {
|
|
265
|
+
// handle error → 500
|
|
290
266
|
}
|
|
291
267
|
```
|
|
292
268
|
|
|
@@ -332,11 +308,41 @@ await send("json-topic", { data: "example" }, { transport });
|
|
|
332
308
|
| Large payloads | StreamTransport | Very Low | Medium |
|
|
333
309
|
| Real-time streams | StreamTransport | Very Low | High |
|
|
334
310
|
|
|
311
|
+
## Handling Empty Queues
|
|
312
|
+
|
|
313
|
+
When no messages are available in the queue, the handler receives `null` for both the message and metadata parameters. This allows graceful handling without exceptions:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
await receive("my-topic", "my-consumer", async (message, metadata) => {
|
|
317
|
+
if (!message) {
|
|
318
|
+
console.log("No message received - queue is empty");
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Process the message
|
|
323
|
+
console.log("Processing:", message);
|
|
324
|
+
console.log("Message ID:", metadata.messageId);
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
The same pattern works with `handleCallback`:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { handleCallback } from "@vercel/queue/web";
|
|
332
|
+
|
|
333
|
+
export const POST = handleCallback(async (message, metadata) => {
|
|
334
|
+
if (!message) {
|
|
335
|
+
// No message available - handle gracefully
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
await processMessage(message);
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
335
342
|
## Error Handling
|
|
336
343
|
|
|
337
344
|
The queue client provides specific error types:
|
|
338
345
|
|
|
339
|
-
- **`QueueEmptyError`**: No messages available in the queue
|
|
340
346
|
- **`MessageLockedError`**: Message is being processed by another consumer
|
|
341
347
|
- **`MessageNotFoundError`**: Message doesn't exist or has expired
|
|
342
348
|
- **`MessageNotAvailableError`**: Message exists but cannot be claimed
|
|
@@ -346,7 +352,6 @@ The queue client provides specific error types:
|
|
|
346
352
|
- **`UnauthorizedError`**: Authentication failed (invalid or missing token)
|
|
347
353
|
- **`ForbiddenError`**: Access denied (wrong environment or project)
|
|
348
354
|
- **`DuplicateMessageError`**: Idempotency key was already used
|
|
349
|
-
- **`ConcurrencyLimitError`**: Too many in-flight messages
|
|
350
355
|
- **`ConsumerDiscoveryError`**: Could not reach the consumer deployment
|
|
351
356
|
- **`ConsumerRegistryNotConfiguredError`**: Project not configured for queues
|
|
352
357
|
- **`InternalServerError`**: Unexpected server error
|
|
@@ -357,7 +362,6 @@ Example error handling:
|
|
|
357
362
|
```typescript
|
|
358
363
|
import {
|
|
359
364
|
BadRequestError,
|
|
360
|
-
ConcurrencyLimitError,
|
|
361
365
|
DuplicateMessageError,
|
|
362
366
|
ForbiddenError,
|
|
363
367
|
InternalServerError,
|
|
@@ -375,13 +379,6 @@ try {
|
|
|
375
379
|
console.log("Invalid parameters:", error.message);
|
|
376
380
|
} else if (error instanceof DuplicateMessageError) {
|
|
377
381
|
console.log("Duplicate message:", error.idempotencyKey);
|
|
378
|
-
} else if (error instanceof ConcurrencyLimitError) {
|
|
379
|
-
console.log(
|
|
380
|
-
"Rate limited:",
|
|
381
|
-
error.currentInflight,
|
|
382
|
-
"/",
|
|
383
|
-
error.maxConcurrency,
|
|
384
|
-
);
|
|
385
382
|
} else if (error instanceof InternalServerError) {
|
|
386
383
|
console.log("Server error - retry with backoff");
|
|
387
384
|
}
|
|
@@ -408,26 +405,32 @@ The following environment variables can be used to configure the queue client:
|
|
|
408
405
|
```typescript
|
|
409
406
|
import { receive } from "@vercel/queue";
|
|
410
407
|
|
|
411
|
-
// Process next available message
|
|
412
|
-
await receive<T>(topicName, consumerGroup,
|
|
408
|
+
// Process next available message (or null if queue is empty)
|
|
409
|
+
await receive<T>(topicName, consumerGroup, async (message, metadata) => {
|
|
410
|
+
if (!message) {
|
|
411
|
+
console.log("Queue is empty");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
// Process message
|
|
415
|
+
});
|
|
413
416
|
|
|
414
|
-
//
|
|
417
|
+
// Batch processing: fetch up to 10 messages in one request
|
|
415
418
|
await receive<T>(topicName, consumerGroup, handler, {
|
|
416
|
-
|
|
419
|
+
limit: 10, // Default: 1, Min: 1, Max: 10
|
|
417
420
|
});
|
|
418
421
|
|
|
419
|
-
// Process message
|
|
422
|
+
// Process specific message by ID
|
|
420
423
|
await receive<T>(topicName, consumerGroup, handler, {
|
|
421
|
-
messageId: "message-id",
|
|
422
|
-
transport: new JsonTransport(), // Optional: custom transport (defaults to JsonTransport)
|
|
423
|
-
visibilityTimeoutSeconds: 30, // Optional: message visibility timeout
|
|
424
|
-
visibilityRefreshInterval: 10, // Optional: how often to refresh the lock
|
|
424
|
+
messageId: "message-id",
|
|
425
425
|
});
|
|
426
426
|
|
|
427
|
+
// Note: limit and messageId are mutually exclusive options
|
|
428
|
+
|
|
427
429
|
// Handler function signature
|
|
430
|
+
// When queue is empty, both message and metadata are null
|
|
428
431
|
type MessageHandler<T = unknown> = (
|
|
429
|
-
message: T,
|
|
430
|
-
metadata: MessageMetadata,
|
|
432
|
+
message: T | null,
|
|
433
|
+
metadata: MessageMetadata | null,
|
|
431
434
|
) => Promise<void> | void;
|
|
432
435
|
|
|
433
436
|
// MessageMetadata type
|
|
@@ -446,7 +449,7 @@ interface MessageMetadata {
|
|
|
446
449
|
|
|
447
450
|
| Limit | Value | Notes |
|
|
448
451
|
| --------------------------- | --------------------- | ----------------------------------- |
|
|
449
|
-
| Message throughput | 10,
|
|
452
|
+
| Message throughput | 10,000+ msg/sec/topic | Scales horizontally |
|
|
450
453
|
| Payload size | 1 GB | Smaller messages have lower latency |
|
|
451
454
|
| Number of topics | Unlimited | No hard limit |
|
|
452
455
|
| Consumer groups per message | ~4,000 | Per-message limit |
|
|
@@ -464,11 +467,10 @@ interface MessageMetadata {
|
|
|
464
467
|
|
|
465
468
|
#### Receiving Messages
|
|
466
469
|
|
|
467
|
-
| Parameter | Default
|
|
468
|
-
| -------------------------- |
|
|
469
|
-
| `visibilityTimeoutSeconds` | 30
|
|
470
|
-
| `limit` | 1
|
|
471
|
-
| `maxConcurrency` | unlimited | 1 | 10,000 | In-flight message limit |
|
|
470
|
+
| Parameter | Default | Min | Max | Notes |
|
|
471
|
+
| -------------------------- | ------- | --- | ----- | --------------------------- |
|
|
472
|
+
| `visibilityTimeoutSeconds` | 30 | 0 | 3,600 | 0 = immediate re-visibility |
|
|
473
|
+
| `limit` | 1 | 1 | 10 | Messages per request |
|
|
472
474
|
|
|
473
475
|
#### Visibility Extension
|
|
474
476
|
|
|
@@ -505,7 +507,7 @@ Topic patterns support wildcards for flexible routing:
|
|
|
505
507
|
"app/api/queue/route.ts": {
|
|
506
508
|
"experimentalTriggers": [
|
|
507
509
|
{
|
|
508
|
-
"type": "queue/
|
|
510
|
+
"type": "queue/v2beta",
|
|
509
511
|
"topic": "user-*",
|
|
510
512
|
"consumer": "processor"
|
|
511
513
|
}
|
|
@@ -524,12 +526,28 @@ Topic patterns support wildcards for flexible routing:
|
|
|
524
526
|
|
|
525
527
|
## API Reference
|
|
526
528
|
|
|
527
|
-
###
|
|
529
|
+
### Export Structure
|
|
530
|
+
|
|
531
|
+
| Import Path | `handleCallback` |
|
|
532
|
+
| ---------------------------- | ---------------------------------------------------------------- |
|
|
533
|
+
| `@vercel/queue` | Core async function: `(handler, parsed, opts?) => Promise<void>` |
|
|
534
|
+
| `@vercel/queue/web` | Returns `(request: Request) => Promise<Response>` |
|
|
535
|
+
| `@vercel/queue/nextjs/pages` | Returns `(req, res) => Promise<void>` |
|
|
536
|
+
|
|
537
|
+
Additional exports from `@vercel/queue`:
|
|
538
|
+
|
|
539
|
+
| Export | Description |
|
|
540
|
+
| ------------------------- | ------------------------------------------------------------- |
|
|
541
|
+
| `parseCallback` | Parse a Web API `Request` into a `ParsedCallbackRequest` |
|
|
542
|
+
| `parseRawCallback` | Parse a pre-parsed body + headers (e.g. Pages Router) |
|
|
543
|
+
| `CLOUD_EVENT_TYPE_V2BETA` | `"com.vercel.queue.v2beta"` — binary CloudEvent type constant |
|
|
544
|
+
|
|
545
|
+
### QueueClient Configuration
|
|
528
546
|
|
|
529
547
|
```typescript
|
|
530
|
-
import {
|
|
548
|
+
import { QueueClient } from "@vercel/queue";
|
|
531
549
|
|
|
532
|
-
const client = new
|
|
550
|
+
const client = new QueueClient({
|
|
533
551
|
// Base URL for the queue service
|
|
534
552
|
// Default: "https://vercel-queue.com"
|
|
535
553
|
// Env: VERCEL_QUEUE_BASE_URL
|
|
@@ -554,6 +572,10 @@ const client = new Client({
|
|
|
554
572
|
// Default: true
|
|
555
573
|
pinToDeployment: true,
|
|
556
574
|
});
|
|
575
|
+
|
|
576
|
+
// Pass to any function via options
|
|
577
|
+
await send("my-topic", payload, { client });
|
|
578
|
+
export const POST = handleCallback(handler, { client });
|
|
557
579
|
```
|
|
558
580
|
|
|
559
581
|
### Send Options
|
|
@@ -579,62 +601,70 @@ await send("my-topic", payload, {
|
|
|
579
601
|
|
|
580
602
|
### Receive Options
|
|
581
603
|
|
|
604
|
+
The `receive` function supports two mutually exclusive modes:
|
|
605
|
+
|
|
582
606
|
```typescript
|
|
607
|
+
// Batch mode: receive multiple messages
|
|
583
608
|
await receive("my-topic", "my-consumer", handler, {
|
|
584
|
-
//
|
|
585
|
-
|
|
609
|
+
// Maximum messages to retrieve in a single request
|
|
610
|
+
// Default: 1, Min: 1, Max: 10
|
|
611
|
+
limit: 10,
|
|
586
612
|
|
|
587
613
|
// Message lock duration in seconds
|
|
588
|
-
// Default:
|
|
614
|
+
// Default: 300, Min: 30, Max: 3600
|
|
589
615
|
visibilityTimeoutSeconds: 60,
|
|
590
|
-
|
|
591
|
-
// How often to refresh the lock during processing
|
|
592
|
-
// Default: visibilityTimeoutSeconds / 3
|
|
593
|
-
visibilityRefreshInterval: 15,
|
|
594
|
-
|
|
595
|
-
// Custom deserializer (default: JsonTransport)
|
|
596
|
-
transport: new JsonTransport(),
|
|
597
616
|
});
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
### Receive Options (Advanced)
|
|
601
617
|
|
|
602
|
-
|
|
618
|
+
// By-ID mode: receive a specific message
|
|
603
619
|
await receive("my-topic", "my-consumer", handler, {
|
|
604
|
-
//
|
|
605
|
-
|
|
606
|
-
transport: new JsonTransport(),
|
|
620
|
+
// Specific message ID to consume
|
|
621
|
+
messageId: "0-1",
|
|
607
622
|
|
|
608
|
-
// Message lock duration
|
|
609
|
-
// Default:
|
|
623
|
+
// Message lock duration in seconds
|
|
624
|
+
// Default: 300, Min: 30, Max: 3600
|
|
610
625
|
visibilityTimeoutSeconds: 60,
|
|
611
|
-
|
|
612
|
-
// How often to refresh the lock during processing
|
|
613
|
-
// Default: visibilityTimeoutSeconds / 3
|
|
614
|
-
visibilityRefreshInterval: 20,
|
|
615
626
|
});
|
|
616
627
|
```
|
|
617
628
|
|
|
629
|
+
> **Note**: `limit` and `messageId` cannot be used together - they are mutually exclusive options.
|
|
630
|
+
|
|
618
631
|
### handleCallback Options
|
|
619
632
|
|
|
620
633
|
```typescript
|
|
634
|
+
import { handleCallback } from "@vercel/queue/web";
|
|
635
|
+
|
|
621
636
|
export const POST = handleCallback(
|
|
622
|
-
{
|
|
623
|
-
|
|
624
|
-
"my-consumer": async (message, metadata) => {
|
|
625
|
-
await processMessage(message);
|
|
626
|
-
},
|
|
627
|
-
},
|
|
637
|
+
async (message, metadata) => {
|
|
638
|
+
await processMessage(message);
|
|
628
639
|
},
|
|
629
640
|
{
|
|
630
641
|
// Message lock duration for long-running handlers
|
|
631
|
-
// Default:
|
|
632
|
-
// visibilityRefreshInterval defaults to visibilityTimeoutSeconds / 3
|
|
642
|
+
// Default: 300, Min: 30, Max: 3600
|
|
633
643
|
visibilityTimeoutSeconds: 300, // 5 minutes
|
|
634
644
|
},
|
|
635
645
|
);
|
|
636
646
|
```
|
|
637
647
|
|
|
648
|
+
### Core handleCallback
|
|
649
|
+
|
|
650
|
+
The core `handleCallback` is an async function that takes already-parsed request data. Use it to build custom framework integrations:
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
import { handleCallback, parseCallback, parseRawCallback } from "@vercel/queue";
|
|
654
|
+
|
|
655
|
+
// Web API Request
|
|
656
|
+
const parsed = await parseCallback(request);
|
|
657
|
+
|
|
658
|
+
// Or, for frameworks that pre-parse the body (e.g. Pages Router)
|
|
659
|
+
const parsed = parseRawCallback(req.body, req.headers);
|
|
660
|
+
|
|
661
|
+
try {
|
|
662
|
+
await handleCallback(handler, parsed);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
// handle error → 500
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
638
668
|
## License
|
|
639
669
|
|
|
640
670
|
MIT
|