Skip to content

Commit 21d27c0

Browse files
Fix Queues docs code examples & types (#28192)
* fix(queues): update all code examples for type correctness and best practices - Fix MessageSendRequest type to match @cloudflare/workers-types (contentType and delaySeconds are direct properties, not nested under options) - Add readonly modifier to MessageBatch.messages in type definitions - Use satisfies ExportedHandler<Env> pattern consistently across all examples - Replace let with const where values are not reassigned - Fix Error type issues: native Error objects are not structured-clone serializable, use serializable ErrorMessage interface instead - Add proper error type narrowing in catch blocks (instanceof Error) - Remove e: any catch annotations in favor of proper type narrowing - Fix missing return statements in fetch handlers - Fix Durable Objects example: use DurableObject base class instead of deprecated implements DurableObject pattern, add missing closing brace - Replace Queue<any> with properly typed Queue<Message> throughout - Use interface instead of type for Env declarations consistently - Fix sendBatch example that unnecessarily JSON.stringified message bodies - Update AI skill reference to match corrected MessageSendRequest type * fix(queues): complete implicit snippets and add missing Env interfaces in how-queues-works --------- Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
1 parent 80079b4 commit 21d27c0

9 files changed

Lines changed: 170 additions & 128 deletions

File tree

src/content/docs/queues/configuration/batching-retries.mdx

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ To explicitly acknowledge a message as delivered, call the `ack()` method on the
6666
<TypeScriptExample filename="index.ts" omitTabs={true}>
6767
```ts
6868
export default {
69-
async queue(batch: MessageBatch, env: Env, ctx: ExecutionContext) {
69+
async queue(batch, env, ctx): Promise<void> {
7070
for (const msg of batch.messages) {
7171
// TODO: do something with the message
7272
// Explicitly acknowledge the message as delivered
7373
msg.ack();
7474
}
7575
},
76-
};
76+
} satisfies ExportedHandler<Env>;
7777
```
7878
</TypeScriptExample>
7979
<TabItem label="Python" icon="seti:python">
@@ -96,13 +96,13 @@ You can also call `retry()` to explicitly force a message to be redelivered in a
9696
<TypeScriptExample filename="index.ts" omitTabs={true}>
9797
```ts
9898
export default {
99-
async queue(batch: MessageBatch, env: Env, ctx: ExecutionContext) {
99+
async queue(batch, env, ctx): Promise<void> {
100100
for (const msg of batch.messages) {
101101
// TODO: do something with the message that fails
102102
msg.retry();
103103
}
104104
},
105-
};
105+
} satisfies ExportedHandler<Env>;
106106
```
107107
</TypeScriptExample>
108108
<TabItem label="Python" icon="seti:python">
@@ -210,14 +210,14 @@ To delay an individual message within a batch:
210210
<TypeScriptExample filename="index.ts" omitTabs={true}>
211211
```ts
212212
export default {
213-
async queue(batch: MessageBatch, env: Env, ctx: ExecutionContext) {
213+
async queue(batch, env, ctx): Promise<void> {
214214
for (const msg of batch.messages) {
215215
// Mark for retry and delay a singular message
216216
// by 3600 seconds (1 hour)
217217
msg.retry({ delaySeconds: 3600 });
218218
}
219219
},
220-
};
220+
} satisfies ExportedHandler<Env>;
221221
```
222222
</TypeScriptExample>
223223
<TabItem label="Python" icon="seti:python">
@@ -240,12 +240,12 @@ To delay a batch of messages:
240240
<TypeScriptExample filename="index.ts" omitTabs={true}>
241241
```ts
242242
export default {
243-
async queue(batch: MessageBatch, env: Env, ctx: ExecutionContext) {
243+
async queue(batch, env, ctx): Promise<void> {
244244
// Mark for retry and delay a batch of messages
245245
// by 600 seconds (10 minutes)
246246
batch.retryAll({ delaySeconds: 600 });
247247
},
248-
};
248+
} satisfies ExportedHandler<Env>;
249249
```
250250
</TypeScriptExample>
251251
<TabItem label="Python" icon="seti:python">
@@ -324,12 +324,12 @@ For example, to generate an [exponential backoff](https://en.wikipedia.org/wiki/
324324
<Tabs syncKey="workersExamples">
325325
<TypeScriptExample filename="index.ts" omitTabs={true}>
326326
```ts
327-
const calculateExponentialBackoff = (
327+
function calculateExponentialBackoff(
328328
attempts: number,
329329
baseDelaySeconds: number,
330-
) => {
330+
): number {
331331
return baseDelaySeconds ** attempts;
332-
};
332+
}
333333
```
334334
</TypeScriptExample>
335335
<TabItem label="Python" icon="seti:python">
@@ -348,10 +348,9 @@ In your consumer, you then pass the value of `msg.attempts` and your desired del
348348
const BASE_DELAY_SECONDS = 30;
349349

350350
export default {
351-
async queue(batch: MessageBatch, env: Env, ctx: ExecutionContext) {
351+
async queue(batch, env, ctx): Promise<void> {
352352
for (const msg of batch.messages) {
353-
// Mark for retry and delay a singular message
354-
// by 3600 seconds (1 hour)
353+
// Mark for retry with exponential backoff
355354
msg.retry({
356355
delaySeconds: calculateExponentialBackoff(
357356
msg.attempts,
@@ -360,7 +359,7 @@ export default {
360359
});
361360
}
362361
},
363-
};
362+
} satisfies ExportedHandler<Env>;
364363
```
365364
</TypeScriptExample>
366365
<TabItem label="Python" icon="seti:python">

src/content/docs/queues/configuration/javascript-apis.mdx

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ An example of writing a single message to a Queue:
2626
<Tabs syncKey="workersExamples">
2727
<TypeScriptExample filename="index.ts" omitTabs={true}>
2828
```ts
29-
type Environment = {
29+
interface Env {
3030
readonly MY_QUEUE: Queue;
31-
};
31+
}
3232

3333
export default {
34-
async fetch(req: Request, env: Environment): Promise<Response> {
34+
async fetch(req, env, ctx): Promise<Response> {
3535
await env.MY_QUEUE.send({
3636
url: req.url,
3737
method: req.method,
3838
headers: Object.fromEntries(req.headers),
3939
});
40-
return new Response('Sent!');
40+
return new Response("Sent!");
4141
},
42-
};
42+
} satisfies ExportedHandler<Env>;
4343
```
4444
</TypeScriptExample>
4545
<TabItem label="Python" icon="seti:python">
@@ -64,25 +64,24 @@ The Queues API also supports writing multiple messages at once:
6464
<Tabs syncKey="workersExamples">
6565
<TypeScriptExample filename="index.ts" omitTabs={true}>
6666
```ts
67-
const sendResultsToQueue = async (results: Array<any>, env: Environment) => {
68-
const batch: MessageSendRequest[] = results.map((value) => ({
69-
body: JSON.stringify(value),
70-
}));
71-
await env.queue.sendBatch(batch);
67+
const sendResultsToQueue = async (results: Array<unknown>, env: Env) => {
68+
const batch: MessageSendRequest[] = results.map((value) => ({
69+
body: value,
70+
}));
71+
await env.MY_QUEUE.sendBatch(batch);
7272
};
7373
```
7474
</TypeScriptExample>
7575
<TabItem label="Python" icon="seti:python">
7676
```python
77-
import json
7877
from pyodide.ffi import to_js
7978

8079
async def send_results_to_queue(results, env):
8180
batch = [
82-
{"body": json.dumps(value)}
81+
{"body": value}
8382
for value in results
8483
]
85-
await env.queue.sendBatch(to_js(batch))
84+
await env.MY_QUEUE.sendBatch(to_js(batch))
8685
```
8786
</TabItem>
8887
</Tabs>
@@ -105,9 +104,10 @@ interface Queue<Body = unknown> {
105104
* Sends a message to the Queue. The body can be any type supported by the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types), as long as its size is less than 128 KB.
106105
* When the promise resolves, the message is confirmed to be written to disk.
107106

108-
* `sendBatch(bodyIterable<MessageSendRequest<unknown>>)` <Type text="Promise<void>" />
107+
* `sendBatch(messagesIterable<MessageSendRequest<unknown>>, options?QueueSendBatchOptions)` <Type text="Promise<void>" />
109108

110109
* Sends a batch of messages to the Queue. Each item in the provided [Iterable](https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html) must be supported by the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types). A batch can contain up to 100 messages, though items are limited to 128 KB each, and the total size of the array cannot exceed 256 KB.
110+
* The optional `options` parameter can be used to apply settings (such as `delaySeconds`) to all messages in the batch. See [QueueSendBatchOptions](#queuesendbatchoptions).
111111
* When the promise resolves, the messages are confirmed to be written to disk.
112112

113113

@@ -117,10 +117,11 @@ interface Queue<Body = unknown> {
117117
A wrapper type used for sending message batches.
118118

119119
```ts
120-
type MessageSendRequest<Body = unknown> = {
120+
interface MessageSendRequest<Body = unknown> {
121121
body: Body;
122-
options?: QueueSendOptions;
123-
};
122+
contentType?: QueueContentType;
123+
delaySeconds?: number;
124+
}
124125
```
125126

126127

@@ -130,9 +131,15 @@ type MessageSendRequest<Body = unknown> = {
130131
* The body of the message.
131132
* The body can be any type supported by the [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types), as long as its size is less than 128 KB.
132133

133-
* <code>options</code> <Type text="QueueSendOptions" />
134+
* <code>contentType</code> <Type text="QueueContentType" />
134135

135-
* Options to apply to the current message, including content type and message delay settings.
136+
* The explicit content type of a message so it can be previewed correctly with the [List messages from the dashboard](/queues/examples/list-messages-from-dash/) feature. Optional argument.
137+
* See [QueuesContentType](#queuescontenttype) for possible values.
138+
139+
* <code>delaySeconds</code> <Type text="number" />
140+
141+
* The number of seconds to [delay a message](/queues/configuration/batching-retries/) for within the queue, before it can be delivered to a consumer.
142+
* Must be an integer between 0 and 43200 (12 hours).
136143

137144

138145

@@ -209,17 +216,17 @@ If the `queue()` function throws, or the promise returned by it or any of the pr
209216
<Tabs syncKey="workersExamples">
210217
<TypeScriptExample filename="index.ts" omitTabs={true}>
211218
```ts
219+
interface Env {
220+
// Add your bindings here
221+
}
222+
212223
export default {
213-
async queue(
214-
batch: MessageBatch,
215-
env: Environment,
216-
ctx: ExecutionContext
217-
): Promise<void> {
224+
async queue(batch, env, ctx): Promise<void> {
218225
for (const message of batch.messages) {
219-
console.log('Received', message);
226+
console.log("Received", message.body);
220227
}
221228
},
222-
};
229+
} satisfies ExportedHandler<Env>;
223230
```
224231
</TypeScriptExample>
225232
<TabItem label="Python" icon="seti:python">
@@ -261,7 +268,7 @@ A batch of messages that are sent to a consumer Worker.
261268
```ts
262269
interface MessageBatch<Body = unknown> {
263270
readonly queue: string;
264-
readonly messages: Message<Body>[];
271+
readonly messages: readonly Message<Body>[];
265272
ackAll(): void;
266273
retryAll(options?: QueueRetryOptions): void;
267274
}

src/content/docs/queues/examples/publish-to-a-queue-via-workers.mdx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,26 @@ interface Env {
5454
}
5555

5656
export default {
57-
async fetch(req, env): Promise<Response> {
57+
async fetch(req, env, ctx): Promise<Response> {
5858
// Validate the payload is JSON
5959
// In a production application, we may more robustly validate the payload
6060
// against a schema using a library like 'zod'
6161
let messages;
6262
try {
6363
messages = await req.json();
64-
} catch (e) {
64+
} catch {
6565
// Return a HTTP 400 (Bad Request) if the payload isn't JSON
66-
return Response.json({ err: "payload not valid JSON" }, { status: 400 });
66+
return Response.json({ error: "payload not valid JSON" }, { status: 400 });
6767
}
6868

6969
// Publish to the Queue
7070
try {
7171
await env.YOUR_QUEUE.send(messages);
72-
} catch (e: any) {
73-
console.log(`failed to send to the queue: ${e}`);
72+
} catch (e) {
73+
const message = e instanceof Error ? e.message : "Unknown error";
74+
console.error(`failed to send to the queue: ${message}`);
7475
// Return a HTTP 500 (Internal Error) if our publish operation fails
75-
return Response.json({ error: e.message }, { status: 500 });
76+
return Response.json({ error: message }, { status: 500 });
7677
}
7778

7879
// Return a HTTP 200 if the send succeeded!

src/content/docs/queues/examples/send-errors-to-r2.mdx

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,35 +49,44 @@ The following Worker will catch JavaScript errors and send them to a queue. The
4949
</WranglerConfig>
5050

5151
```ts
52-
type Environment = {
53-
readonly ERROR_QUEUE: Queue<Error>;
52+
interface ErrorMessage {
53+
message: string;
54+
stack?: string;
55+
}
56+
57+
interface Env {
58+
readonly ERROR_QUEUE: Queue<ErrorMessage>;
5459
readonly ERROR_BUCKET: R2Bucket;
55-
};
60+
}
5661

5762
export default {
58-
async fetch(req, env): Promise<Response> {
63+
async fetch(req, env, ctx): Promise<Response> {
5964
try {
6065
return doRequest(req);
61-
} catch (error) {
66+
} catch (e) {
67+
const error: ErrorMessage = {
68+
message: e instanceof Error ? e.message : String(e),
69+
stack: e instanceof Error ? e.stack : undefined,
70+
};
6271
await env.ERROR_QUEUE.send(error);
6372
return new Response(error.message, { status: 500 });
6473
}
6574
},
66-
async queue(batch, env): Promise<void> {
67-
let file = '';
75+
async queue(batch, env, ctx): Promise<void> {
76+
let file = "";
6877
for (const message of batch.messages) {
6978
const error = message.body;
70-
file += error.stack || error.message || String(error);
71-
file += '\r\n';
79+
file += error.stack ?? error.message;
80+
file += "\r\n";
7281
}
7382
await env.ERROR_BUCKET.put(`errors/${Date.now()}.log`, file);
7483
},
75-
} satisfies ExportedHandler<Environment, Error>;
84+
} satisfies ExportedHandler<Env, ErrorMessage>;
7685

77-
function doRequest(request: Request): Promise<Response> {
86+
function doRequest(request: Request): Response {
7887
if (Math.random() > 0.5) {
79-
return new Response('Success!');
88+
return new Response("Success!");
8089
}
81-
throw new Error('Failed!');
90+
throw new Error("Failed!");
8291
}
8392
```

src/content/docs/queues/examples/use-queues-with-durable-objects.mdx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,31 +64,33 @@ The following Worker script:
6464
2. Passes request data to the Durable Object.
6565
3. Publishes to a queue from within the Durable Object.
6666

67-
The `constructor()` in the Durable Object makes your `Environment` available (in scope) on `this.env` to the [`fetch()` handler](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/) in the Durable Object.
67+
Extending the `DurableObject` base class makes your `Env` available on `this.env` and the Durable Object state available on `this.ctx` within the [`fetch()` handler](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/) in the Durable Object.
6868

6969
```ts
70+
import { DurableObject } from "cloudflare:workers";
71+
7072
interface Env {
7173
YOUR_QUEUE: Queue;
72-
YOUR_DO_CLASS: DurableObjectNamespace;
74+
YOUR_DO_CLASS: DurableObjectNamespace<YourDurableObject>;
7375
}
7476

7577
export default {
76-
async fetch(req, env): Promise<Response> {
78+
async fetch(req, env, ctx): Promise<Response> {
7779
// Assume each Durable Object is mapped to a userId in a query parameter
7880
// In a production application, this will be a userId defined by your application
7981
// that you validate (and/or authenticate) first.
80-
let url = new URL(req.url)
81-
let userIdParam = url.searchParams.get("userId")
82+
const url = new URL(req.url);
83+
const userIdParam = url.searchParams.get("userId");
8284

8385
if (userIdParam) {
8486
// Get a stub that allows you to call that Durable Object
85-
let durableObjectStub = env.YOUR_DO_CLASS.getByName(userIdParam);
87+
const durableObjectStub = env.YOUR_DO_CLASS.getByName(userIdParam);
8688

8789
// Pass the request to that Durable Object and await the response
8890
// This invokes the constructor once on your Durable Object class (defined further down)
8991
// on the first initialization, and the fetch method on each request.
9092
// We pass the original Request to the Durable Object's fetch method
91-
let response = await durableObjectStub.fetch(req);
93+
const response = await durableObjectStub.fetch(req);
9294

9395
// This would return "wrote to queue", but you could return any response.
9496
return response;
@@ -97,17 +99,16 @@ export default {
9799
},
98100
} satisfies ExportedHandler<Env>;
99101

100-
export class YourDurableObject implements DurableObject {
101-
constructor(private state: DurableObjectState, private env: Env) {}
102-
102+
export class YourDurableObject extends DurableObject<Env> {
103103
async fetch(req: Request): Promise<Response> {
104104
// Error handling elided for brevity.
105105
// Publish to your queue
106106
await this.env.YOUR_QUEUE.send({
107-
id: this.state.id.toString() // Write the ID of the Durable Object to your queue
107+
id: this.ctx.id.toString(), // Write the ID of the Durable Object to your queue
108108
// Write any other properties to your queue
109109
});
110110

111-
return new Response("wrote to queue")
111+
return new Response("wrote to queue");
112112
}
113+
}
113114
```

0 commit comments

Comments
 (0)