Skip to content
Files SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Cloudflare R2

Send R2 event notifications to a Queue and dispatch them from a Worker consumer.

R2 delivers notifications to a Queue. Consume it from a Worker and hand each message body to dispatch().

npx wrangler queues create r2-events
npx wrangler r2 bucket notification create my-bucket \
  --event-type object-create --event-type object-delete \
  --queue r2-events

object-create covers PutObject, CopyObject and CompleteMultipartUpload; object-delete covers DeleteObject and lifecycle deletions. --prefix and --suffix narrow a rule.

{
  "queues": {
    "consumers": [
      {
        "queue": "r2-events",
        "max_retries": 3,
        "dead_letter_queue": "r2-events-dlq",
      },
    ],
  },
}
import { createFiles } from "files-sdk";
import { events } from "files-sdk/events";
import { r2 } from "files-sdk/r2";

export default {
  async queue(batch, env) {
    const files = createFiles({
      adapter: r2({ binding: env.BUCKET }),
      plugins: [events()],
    });
    files.events.on("created", "uploads/**", (event) =>
      indexUpload(env, event)
    );

    for (const message of batch.messages) {
      try {
        await files.events.dispatch(message.body);
        message.ack();
      } catch {
        message.retry();
      }
    }
  },
} satisfies ExportedHandler<Env>;

Without a dead-letter queue, a message that keeps failing is dropped after max_retries.

Pull consumers

Outside Workers, pull from the queue over HTTP and pass the response (or each message) to dispatch(). Pulled bodies arrive as strings, sometimes base64-encoded; both are decoded.

What you get

R2 notifications carry the key, size and ETag (none on deletes) and the event time. There’s no event id, so event.id is built from the key, ETag and time.

Last updated on

Was this page helpful?