@vercel/queue 0.0.0-alpha.2 → 0.0.0-alpha.3

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 CHANGED
@@ -1,12 +1,17 @@
1
1
  # Vercel Queues
2
2
 
3
- A TypeScript client library for interacting with the Vercel Queue Service API with customizable serialization/deserialization (transport) support, including **streaming support** for memory-efficient processing of large payloads.
3
+ A TypeScript client library for interacting with the Vercel Queue Service API
4
+ with customizable serialization/deserialization (transport) support, including
5
+ **streaming support** for memory-efficient processing of large payloads.
4
6
 
5
7
  ## Features
6
8
 
7
- - **Generic Payload Support**: Send and receive any type of data with type safety
8
- - **Customizable Serialization**: Use built-in transports (JSON, Buffer, Stream) or create your own
9
- - **Streaming Support**: Handle large payloads without loading them entirely into memory
9
+ - **Generic Payload Support**: Send and receive any type of data with type
10
+ safety
11
+ - **Customizable Serialization**: Use built-in transports (JSON, Buffer, Stream)
12
+ or create your own
13
+ - **Streaming Support**: Handle large payloads without loading them entirely
14
+ into memory
10
15
  - **Pub/Sub Pattern**: Topic-based messaging with consumer groups
11
16
  - **Type Safety**: Full TypeScript support with generic types
12
17
  - **Automatic Retries**: Built-in visibility timeout management
@@ -19,7 +24,8 @@ npm install @vercel/queue
19
24
 
20
25
  ## Quick Start
21
26
 
22
- For local development, you'll need to pull your Vercel environment variables (including the OIDC token):
27
+ For local development, you'll need to pull your Vercel environment variables
28
+ (including the OIDC token):
23
29
 
24
30
  ```bash
25
31
  # Install Vercel CLI if you haven't already
@@ -33,10 +39,10 @@ Publishing and consuming messages on a queue
33
39
 
34
40
  ```typescript
35
41
  // index.ts
36
- import { QueueClient, createTopic, JsonTransport } from "@vercel/queue";
42
+ import { createTopic, JsonTransport, QueueClient } from "@vercel/queue";
37
43
 
38
44
  // Create a client - automatically authenticated using the OIDC token
39
- const client = QueueClient.fromVercelFunction();
45
+ const client = await QueueClient.fromVercelFunction();
40
46
 
41
47
  // Create a topic with JSON serialization (default)
42
48
  const topic = createTopic<{ message: string; timestamp: number }>(
@@ -79,13 +85,18 @@ dotenv -e .env.local node index.ts
79
85
 
80
86
  ## Usage with Vercel
81
87
 
82
- When deploying on Vercel, rather than having a persistent server subscribed to a queue, Vercel can trigger a callback route when a message is ready for consumption.
88
+ When deploying on Vercel, rather than having a persistent server subscribed to a
89
+ queue, Vercel can trigger a callback route when a message is ready for
90
+ consumption.
83
91
 
84
- To demonstrate using queues on Vercel, let's use a Next.js app. You can use an existing app or create one using [create-next-app](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
92
+ To demonstrate using queues on Vercel, let's use a Next.js app. You can use an
93
+ existing app or create one using
94
+ [create-next-app](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
85
95
 
86
96
  ### TypeScript Configuration
87
97
 
88
- Update your `tsconfig.json` to use `"bundler"` module resolution for proper package export resolution:
98
+ Update your `tsconfig.json` to use `"bundler"` module resolution for proper
99
+ package export resolution:
89
100
 
90
101
  ```json
91
102
  {
@@ -104,7 +115,7 @@ Create a new server function to publish messages
104
115
  // app/action.ts
105
116
  "use server";
106
117
 
107
- import { QueueClient, createTopic } from "@vercel/queue";
118
+ import { createTopic, QueueClient } from "@vercel/queue";
108
119
 
109
120
  export async function publishTestMessage(message: string) {
110
121
  // Initialize a queue client
@@ -121,12 +132,10 @@ export async function publishTestMessage(message: string) {
121
132
  { message, timestamp: Date.now() },
122
133
  {
123
134
  // Provide a callback URL to invoke a consumer when the message is ready to be processed
124
- callbacks: {
125
- webhook: {
126
- url: process.env.VERCEL_PROJECT_PRODUCTION_URL
127
- ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}/api/queue/handle`
128
- : "http://localhost:3000/api/queue/handle",
129
- },
135
+ callback: {
136
+ url: process.env.VERCEL_PROJECT_PRODUCTION_URL
137
+ ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}/api/queue/handle`
138
+ : "http://localhost:3000/api/queue/handle",
130
139
  },
131
140
  },
132
141
  );
@@ -154,65 +163,45 @@ export default function Button() {
154
163
 
155
164
  ### Consuming the queue
156
165
 
157
- Instead of running a persistent server that subscribes to the queue, we use the callback functionality of Vercel queues to consume messages on the fly, when a message is ready to be processed.
166
+ Instead of running a persistent server that subscribes to the queue, we use the
167
+ callback functionality of Vercel queues to consume messages on the fly, when a
168
+ message is ready to be processed.
169
+
170
+ The `handleCallback` helper function simplifies queue callback handling in NextJS:
158
171
 
159
172
  ```typescript
160
173
  // app/api/queue/handle/route.ts
161
- import { QueueClient, Topic, parseCallbackRequest } from "@vercel/queue";
162
- import { NextRequest } from "next/server";
163
-
164
- // Handle Vercel Queue callback requests
165
- export async function POST(request: NextRequest) {
166
- try {
167
- // Parse the queue callback information
168
- const { queueName, consumerGroup, messageId } =
169
- parseCallbackRequest(request);
170
-
171
- // Create client
172
- const client = await QueueClient.fromVercelFunction();
173
-
174
- // Create topic and consumer group from the callback info
175
- const topic = new Topic(client, queueName);
176
- const cg = topic.consumerGroup(consumerGroup);
177
-
178
- // Process the message
179
- await cg.receiveMessage(messageId, async (message) => {
180
- const payload = message.payload as { message: string; timestamp: number };
181
- console.log(
182
- `Received message "${payload.message}" (Sent at: ${payload.timestamp})`,
183
- );
184
- });
174
+ import { handleCallback } from "@vercel/queue";
175
+
176
+ export const POST = handleCallback({
177
+ // Handle messages sent on the "new-users" topic (the consumer
178
+ // group "default" will be used)
179
+ "my-topic": (message, metadata) => {
180
+ console.log(`Received message:`, message, metadata);
181
+ // metadata: { messageId, deliveryCount, timestamp }
182
+ },
183
+ });
185
184
 
186
- return Response.json({ status: "success" });
187
- } catch (error) {
188
- console.error("Webhook error:", error);
189
- return Response.json(
190
- { error: "Failed to process webhook" },
191
- { status: 500 },
192
- );
193
- }
194
- }
185
+ // Or, specify separate handlers for separate consumer groups
186
+ export const POST = handleCallback({
187
+ // topic: "my-topic"
188
+ "my-topic": {
189
+ // consumer group: "compress"
190
+ compress: (message, metadata) => {
191
+ console.log("Compressing image:", message);
192
+ },
193
+ // consumer group: "resize"
194
+ resize: (message, metadata) => {
195
+ console.log("Resizing image:", message);
196
+ },
197
+ // consumer group: "watermark"
198
+ watermark: (message, metadata) => {
199
+ console.log("Adding watermark:", message);
200
+ },
201
+ },
202
+ });
195
203
  ```
196
204
 
197
- > ![NOTE]
198
- > A single webhook handle can be used to process messages across various queues and consumer groups. Use the values of `queueName` and `consumerGroup` from `parseCallbackRequest()` to dynamically handle different code paths:
199
- >
200
- > ```typescript
201
- > if (queueName === "upload-queue") {
202
- > processImageQueue(consumerGroup, message);
203
- > }
204
- > // ...
205
- > function processImageQueue(consumerGroup, message) {
206
- > if (consumerGroup === "compress") {
207
- > // handle image compression
208
- > }
209
- > // ...
210
- > }
211
- > // ...
212
- > ```
213
- >
214
- > We are building an SDK to make queues and workflow easier to use. Reach out if you're interested.
215
-
216
205
  ## Key Features
217
206
 
218
207
  ### Streaming Support
@@ -261,12 +250,14 @@ const webhooks = topic.consumerGroup("webhooks");
261
250
  ## Architecture
262
251
 
263
252
  - **Topics**: Named message channels with configurable serialization
264
- - **Consumer Groups**: Named groups of consumers that process messages in parallel
253
+ - **Consumer Groups**: Named groups of consumers that process messages in
254
+ parallel
265
255
  - `subscribe()`: Continuously process messages with automatic polling
266
256
  - `receiveMessage()`: Process a specific message by ID
267
257
  - `receiveNextMessage()`: Process the next available message (one-shot)
268
258
  - `handleMessage()`: Process message metadata only (without payload)
269
- - **Transports**: Pluggable serialization/deserialization for different data types
259
+ - **Transports**: Pluggable serialization/deserialization for different data
260
+ types
270
261
  - **Streaming**: Memory-efficient processing of large payloads
271
262
  - **Visibility Timeouts**: Automatic message lifecycle management
272
263
 
@@ -277,20 +268,25 @@ The multipart parser is optimized for high-throughput scenarios:
277
268
  - **Streaming**: Messages are yielded immediately as headers are parsed
278
269
  - **Memory Efficient**: No buffering of complete payloads
279
270
  - **Fast Parsing**: Native Buffer operations for ~50% performance improvement
280
- - **Scalable**: Can handle arbitrarily large responses without memory constraints
271
+ - **Scalable**: Can handle arbitrarily large responses without memory
272
+ constraints
281
273
 
282
274
  ## Serialization (Transport) System
283
275
 
284
- The queue client supports customizable serialization through the `Transport` interface with **streaming support** for memory-efficient processing. Transport can be configured at the **topic level** when creating a topic, or at the **consumer group level** when creating a consumer group.
276
+ The queue client supports customizable serialization through the `Transport`
277
+ interface with **streaming support** for memory-efficient processing. Transport
278
+ can be configured at the **topic level** when creating a topic, or at the
279
+ **consumer group level** when creating a consumer group.
285
280
 
286
281
  ### Built-in Transports
287
282
 
288
283
  #### JsonTransport (Default)
289
284
 
290
- Buffers data for JSON parsing - suitable for structured data that fits in memory.
285
+ Buffers data for JSON parsing - suitable for structured data that fits in
286
+ memory.
291
287
 
292
288
  ```typescript
293
- import { JsonTransport, createTopic } from "@vercel/queue";
289
+ import { createTopic, JsonTransport } from "@vercel/queue";
294
290
 
295
291
  const topic = createTopic<{ data: any }>(
296
292
  client,
@@ -303,7 +299,8 @@ const topic = createTopic<{ data: any }>(client, "json-topic");
303
299
 
304
300
  #### BufferTransport
305
301
 
306
- Buffers the entire payload into memory as a Buffer - suitable for binary data that fits in memory.
302
+ Buffers the entire payload into memory as a Buffer - suitable for binary data
303
+ that fits in memory.
307
304
 
308
305
  ```typescript
309
306
  import { BufferTransport, createTopic } from "@vercel/queue";
@@ -319,10 +316,11 @@ await topic.publish(binaryData);
319
316
 
320
317
  #### StreamTransport
321
318
 
322
- **True streaming support** - passes ReadableStream directly without buffering. Ideal for large files and memory-efficient processing.
319
+ **True streaming support** - passes ReadableStream directly without buffering.
320
+ Ideal for large files and memory-efficient processing.
323
321
 
324
322
  ```typescript
325
- import { StreamTransport, createTopic } from "@vercel/queue";
323
+ import { createTopic, StreamTransport } from "@vercel/queue";
326
324
 
327
325
  const topic = createTopic<ReadableStream<Uint8Array>>(
328
326
  client,
@@ -346,7 +344,8 @@ await topic.publish(fileStream);
346
344
 
347
345
  ### Custom Transport
348
346
 
349
- You can create your own serialization format by implementing the `Transport` interface:
347
+ You can create your own serialization format by implementing the `Transport`
348
+ interface:
350
349
 
351
350
  ```typescript
352
351
  import { Transport } from "@vercel/queue";
@@ -387,6 +386,21 @@ const topic = createTopic<T>(client, topicName, transport?);
387
386
  // Publish a message (uses topic's transport)
388
387
  await topic.publish(payload, options?);
389
388
 
389
+ // Trigger a callback URL when the message is
390
+ // ready for consumption
391
+ await topic.publish(payload, {
392
+ callback: { url: "https://example.com/webhook" }
393
+ });
394
+
395
+ // Or provide multiple callbacks (each URL is called
396
+ // with a separate consumer group)
397
+ await topic.publish(payload, {
398
+ callback: {
399
+ group1: { url: "https://example.com/webhook1" },
400
+ group2: { url: "https://example.com/webhook2", delay: 30 }
401
+ }
402
+ });
403
+
390
404
  // Create a consumer group (can override transport)
391
405
  const consumer = topic.consumerGroup<U>(groupName, options?);
392
406
  ```
@@ -445,6 +459,42 @@ interface CallbackMessageOptions {
445
459
  consumerGroup: string;
446
460
  messageId: string;
447
461
  }
462
+ // Create a callback handler for NextJS route handlers
463
+ function handleCallback(handlers: CallbackHandlers): (request: Request) => Promise<Response>;
464
+
465
+ // Handler function signature for callbacks
466
+ type Handler<T = unknown> = (
467
+ payload: T,
468
+ metadata: MessageMetadata
469
+ ) => Promise<MessageHandlerResult> | MessageHandlerResult;
470
+
471
+ // Message metadata provided to handlers
472
+ interface MessageMetadata {
473
+ messageId: string;
474
+ deliveryCount: number;
475
+ timestamp: string;
476
+ }
477
+
478
+ // Configuration object with handlers for different topics
479
+ type CallbackHandlers = {
480
+ [topicName: string]:
481
+ | Handler // Single handler (uses 'default' consumer group)
482
+ | { [consumerGroup: string]: Handler }; // Multiple consumer group handlers
483
+ };
484
+
485
+ // Example usage:
486
+ export const POST = handleCallback({
487
+ // Topic handler (uses 'default' consumer group)
488
+ "new-users": (message, metadata) => {
489
+ console.log(`New user event:`, message, metadata);
490
+ },
491
+
492
+ // Consumer group specific handlers
493
+ "image-processing": {
494
+ "compress": (message, metadata) => console.log("Compressing image", message),
495
+ "resize": (message, metadata) => console.log("Resizing image", message),
496
+ }
497
+ });
448
498
 
449
499
  // Error thrown for invalid callback requests
450
500
  class InvalidCallbackError extends Error;
@@ -625,10 +675,11 @@ try {
625
675
 
626
676
  ### Complete Example: Video Processing Pipeline
627
677
 
628
- Here's a comprehensive example showing a video processing pipeline that processes videos with FFmpeg and stores the results in Vercel Blob:
678
+ Here's a comprehensive example showing a video processing pipeline that
679
+ processes videos with FFmpeg and stores the results in Vercel Blob:
629
680
 
630
681
  ```typescript
631
- import { QueueClient, createTopic, StreamTransport } from "@vercel/queue";
682
+ import { createTopic, QueueClient, StreamTransport } from "@vercel/queue";
632
683
  import { spawn } from "child_process";
633
684
  import ffmpeg from "ffmpeg-static";
634
685
  import { put } from "@vercel/blob";
@@ -759,30 +810,39 @@ The queue client provides specific error types for different failure scenarios:
759
810
 
760
811
  ### Error Types
761
812
 
762
- - **`QueueEmptyError`**: Thrown when attempting to receive messages from an empty queue (204 status)
813
+ - **`QueueEmptyError`**: Thrown when attempting to receive messages from an
814
+ empty queue (204 status)
763
815
 
764
816
  - Only thrown when directly using `client.receiveMessages()`
765
- - `ConsumerGroup.subscribe()` handles this error internally and continues polling
817
+ - `ConsumerGroup.subscribe()` handles this error internally and continues
818
+ polling
766
819
 
767
- - **`MessageLockedError`**: Thrown when a message is temporarily locked (423 status)
820
+ - **`MessageLockedError`**: Thrown when a message is temporarily locked (423
821
+ status)
768
822
 
769
823
  - Contains optional `retryAfter` property with seconds to wait before retry
770
- - For `receiveMessages()` on FIFO queues: the next message in sequence is locked
824
+ - For `receiveMessages()` on FIFO queues: the next message in sequence is
825
+ locked
771
826
  - For `receiveMessageById()`: the requested message is locked
772
827
  - `ConsumerGroup.subscribe()` handles this error internally when polling
773
828
 
774
829
  - **`MessageNotFoundError`**: Message doesn't exist (404 status)
775
830
 
776
- - **`MessageNotAvailableError`**: Message exists but isn't available for processing (409 status)
831
+ - **`MessageNotAvailableError`**: Message exists but isn't available for
832
+ processing (409 status)
777
833
 
778
- - **`FifoOrderingViolationError`**: FIFO queue ordering violation (409 status with nextMessageId)
834
+ - **`FifoOrderingViolationError`**: FIFO queue ordering violation (409 status
835
+ with nextMessageId)
779
836
 
780
837
  - Contains `nextMessageId` property indicating which message to process first
781
838
 
782
- - **`FailedDependencyError`**: FIFO ordering violation when receiving by ID (424 status)
839
+ - **`FailedDependencyError`**: FIFO ordering violation when receiving by ID (424
840
+ status)
783
841
 
784
- - Contains `nextMessageId` property indicating which message must be processed first
785
- - Similar to `FifoOrderingViolationError` but specifically for receive-by-ID operations
842
+ - Contains `nextMessageId` property indicating which message must be processed
843
+ first
844
+ - Similar to `FifoOrderingViolationError` but specifically for receive-by-ID
845
+ operations
786
846
 
787
847
  - **`MessageCorruptedError`**: Message data is corrupted or can't be parsed
788
848
 
@@ -805,14 +865,14 @@ The queue client provides specific error types for different failure scenarios:
805
865
 
806
866
  ```typescript
807
867
  import {
808
- QueueEmptyError,
809
- MessageLockedError,
810
- FifoOrderingViolationError,
811
- FailedDependencyError,
812
868
  BadRequestError,
813
- UnauthorizedError,
869
+ FailedDependencyError,
870
+ FifoOrderingViolationError,
814
871
  ForbiddenError,
815
872
  InternalServerError,
873
+ MessageLockedError,
874
+ QueueEmptyError,
875
+ UnauthorizedError,
816
876
  } from "@vercel/queue";
817
877
 
818
878
  // Handle empty queue or locked messages
package/dist/index.d.mts CHANGED
@@ -122,10 +122,12 @@ interface SendMessageOptions<T = unknown> {
122
122
  */
123
123
  retentionSeconds?: number;
124
124
  /**
125
- * Consumer group callback configurations
126
- * Format: { consumerGroup: { url, delay?, frequency? } }
125
+ * Callback configuration
126
+ * - If a single CallbackConfig is provided, it will use the "default" consumer group
127
+ * - If an object is provided, keys are consumer group names with their respective callback configs
128
+ * Format: { consumerGroup: { url, delay?, frequency? } } or { url, delay?, frequency? }
127
129
  */
128
- callbacks?: Record<string, CallbackConfig>;
130
+ callback?: Record<string, CallbackConfig> | CallbackConfig;
129
131
  }
130
132
  interface SendMessageResponse {
131
133
  /**
@@ -652,7 +654,7 @@ declare class Topic<T = unknown> {
652
654
  publish(payload: T, options?: {
653
655
  idempotencyKey?: string;
654
656
  retentionSeconds?: number;
655
- callbacks?: Record<string, CallbackConfig>;
657
+ callback?: Record<string, CallbackConfig> | CallbackConfig;
656
658
  }): Promise<{
657
659
  messageId: string;
658
660
  }>;
@@ -719,7 +721,55 @@ declare function createTopic<T = unknown>(client: QueueClient, topicName: string
719
721
  * ```
720
722
  */
721
723
  declare function parseCallbackRequest(request: Request): CallbackMessageOptions;
724
+ /**
725
+ * Message metadata provided to handlers
726
+ */
727
+ interface MessageMetadata {
728
+ messageId: string;
729
+ deliveryCount: number;
730
+ timestamp: string;
731
+ }
732
+ /**
733
+ * Handler function signature
734
+ */
735
+ type Handler<T = unknown> = (payload: T, metadata: MessageMetadata) => Promise<MessageHandlerResult> | MessageHandlerResult;
736
+ /**
737
+ * Configuration object with handlers for different topics
738
+ * Each topic can have either:
739
+ * - A single handler function (uses 'default' consumer group)
740
+ * - An object with handlers for specific consumer groups
741
+ */
742
+ type CallbackHandlers = {
743
+ [topicName: string]: Handler | {
744
+ [consumerGroup: string]: Handler;
745
+ };
746
+ };
747
+ /**
748
+ * Simplified queue callback handler for NextJS route handlers
749
+ *
750
+ * @param handlers Object with topic-specific handlers
751
+ * @returns A NextJS route handler function
752
+ *
753
+ * @example
754
+ * ```typescript
755
+ * // Topic handler (uses 'default' consumer group)
756
+ * export const POST = handleCallback({
757
+ * "new-users": (message, metadata) => {
758
+ * console.log(`New user event:`, message, metadata);
759
+ * }
760
+ * });
761
+ *
762
+ * // Consumer group specific handlers
763
+ * export const POST = handleCallback({
764
+ * "image-processing": {
765
+ * "compress": (message, metadata) => console.log("Compressing image", message),
766
+ * "resize": (message, metadata) => console.log("Resizing image", message),
767
+ * }
768
+ * });
769
+ * ```
770
+ */
771
+ declare function handleCallback(handlers: CallbackHandlers): (request: Request) => Promise<Response>;
722
772
 
723
773
  declare function getVercelOidcToken(): Promise<string>;
724
774
 
725
- export { BadRequestError, BufferTransport, type CallbackConfig, type CallbackMessageOptions, type ChangeVisibilityOptions, type ChangeVisibilityResponse, ConsumerGroup, type ConsumerGroupOptions, type DeleteMessageOptions, type DeleteMessageResponse, FailedDependencyError, type FifoOrderingError, FifoOrderingViolationError, ForbiddenError, InternalServerError, InvalidCallbackError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveMessageByIdOptions, type ReceiveMessageByIdResponse, type ReceiveMessagesOptions, type SendMessageOptions, type SendMessageResponse, StreamTransport, Topic, type Transport, UnauthorizedError, createTopic, getVercelOidcToken, parseCallbackRequest };
775
+ export { BadRequestError, BufferTransport, type CallbackConfig, type CallbackMessageOptions, type ChangeVisibilityOptions, type ChangeVisibilityResponse, ConsumerGroup, type ConsumerGroupOptions, type DeleteMessageOptions, type DeleteMessageResponse, FailedDependencyError, type FifoOrderingError, FifoOrderingViolationError, ForbiddenError, InternalServerError, InvalidCallbackError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveMessageByIdOptions, type ReceiveMessageByIdResponse, type ReceiveMessagesOptions, type SendMessageOptions, type SendMessageResponse, StreamTransport, Topic, type Transport, UnauthorizedError, createTopic, getVercelOidcToken, handleCallback, parseCallbackRequest };
package/dist/index.d.ts CHANGED
@@ -122,10 +122,12 @@ interface SendMessageOptions<T = unknown> {
122
122
  */
123
123
  retentionSeconds?: number;
124
124
  /**
125
- * Consumer group callback configurations
126
- * Format: { consumerGroup: { url, delay?, frequency? } }
125
+ * Callback configuration
126
+ * - If a single CallbackConfig is provided, it will use the "default" consumer group
127
+ * - If an object is provided, keys are consumer group names with their respective callback configs
128
+ * Format: { consumerGroup: { url, delay?, frequency? } } or { url, delay?, frequency? }
127
129
  */
128
- callbacks?: Record<string, CallbackConfig>;
130
+ callback?: Record<string, CallbackConfig> | CallbackConfig;
129
131
  }
130
132
  interface SendMessageResponse {
131
133
  /**
@@ -652,7 +654,7 @@ declare class Topic<T = unknown> {
652
654
  publish(payload: T, options?: {
653
655
  idempotencyKey?: string;
654
656
  retentionSeconds?: number;
655
- callbacks?: Record<string, CallbackConfig>;
657
+ callback?: Record<string, CallbackConfig> | CallbackConfig;
656
658
  }): Promise<{
657
659
  messageId: string;
658
660
  }>;
@@ -719,7 +721,55 @@ declare function createTopic<T = unknown>(client: QueueClient, topicName: string
719
721
  * ```
720
722
  */
721
723
  declare function parseCallbackRequest(request: Request): CallbackMessageOptions;
724
+ /**
725
+ * Message metadata provided to handlers
726
+ */
727
+ interface MessageMetadata {
728
+ messageId: string;
729
+ deliveryCount: number;
730
+ timestamp: string;
731
+ }
732
+ /**
733
+ * Handler function signature
734
+ */
735
+ type Handler<T = unknown> = (payload: T, metadata: MessageMetadata) => Promise<MessageHandlerResult> | MessageHandlerResult;
736
+ /**
737
+ * Configuration object with handlers for different topics
738
+ * Each topic can have either:
739
+ * - A single handler function (uses 'default' consumer group)
740
+ * - An object with handlers for specific consumer groups
741
+ */
742
+ type CallbackHandlers = {
743
+ [topicName: string]: Handler | {
744
+ [consumerGroup: string]: Handler;
745
+ };
746
+ };
747
+ /**
748
+ * Simplified queue callback handler for NextJS route handlers
749
+ *
750
+ * @param handlers Object with topic-specific handlers
751
+ * @returns A NextJS route handler function
752
+ *
753
+ * @example
754
+ * ```typescript
755
+ * // Topic handler (uses 'default' consumer group)
756
+ * export const POST = handleCallback({
757
+ * "new-users": (message, metadata) => {
758
+ * console.log(`New user event:`, message, metadata);
759
+ * }
760
+ * });
761
+ *
762
+ * // Consumer group specific handlers
763
+ * export const POST = handleCallback({
764
+ * "image-processing": {
765
+ * "compress": (message, metadata) => console.log("Compressing image", message),
766
+ * "resize": (message, metadata) => console.log("Resizing image", message),
767
+ * }
768
+ * });
769
+ * ```
770
+ */
771
+ declare function handleCallback(handlers: CallbackHandlers): (request: Request) => Promise<Response>;
722
772
 
723
773
  declare function getVercelOidcToken(): Promise<string>;
724
774
 
725
- export { BadRequestError, BufferTransport, type CallbackConfig, type CallbackMessageOptions, type ChangeVisibilityOptions, type ChangeVisibilityResponse, ConsumerGroup, type ConsumerGroupOptions, type DeleteMessageOptions, type DeleteMessageResponse, FailedDependencyError, type FifoOrderingError, FifoOrderingViolationError, ForbiddenError, InternalServerError, InvalidCallbackError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveMessageByIdOptions, type ReceiveMessageByIdResponse, type ReceiveMessagesOptions, type SendMessageOptions, type SendMessageResponse, StreamTransport, Topic, type Transport, UnauthorizedError, createTopic, getVercelOidcToken, parseCallbackRequest };
775
+ export { BadRequestError, BufferTransport, type CallbackConfig, type CallbackMessageOptions, type ChangeVisibilityOptions, type ChangeVisibilityResponse, ConsumerGroup, type ConsumerGroupOptions, type DeleteMessageOptions, type DeleteMessageResponse, FailedDependencyError, type FifoOrderingError, FifoOrderingViolationError, ForbiddenError, InternalServerError, InvalidCallbackError, InvalidLimitError, JsonTransport, type Message, MessageCorruptedError, type MessageHandler, type MessageHandlerResult, MessageLockedError, type MessageMetadata, MessageNotAvailableError, MessageNotFoundError, type MessageTimeoutResult, QueueClient, type QueueClientOptions, QueueEmptyError, type ReceiveMessageByIdOptions, type ReceiveMessageByIdResponse, type ReceiveMessagesOptions, type SendMessageOptions, type SendMessageResponse, StreamTransport, Topic, type Transport, UnauthorizedError, createTopic, getVercelOidcToken, handleCallback, parseCallbackRequest };