Skip to content

backpine/building-with-cloudflare-queues

 
 

Repository files navigation

Building with Cloudflare Queues

Watch the video

A demonstration monorepo showcasing how to build resilient distributed systems using Cloudflare Queues. This project implements a producer-consumer architecture with AI-powered message processing and R2 event notifications.

Overview

This project demonstrates key concepts of message queues:

  • Producers send data to a queue and respond immediately
  • Consumers process data asynchronously with retry logic
  • Event Subscriptions trigger processing from Cloudflare services (R2, D1, etc.)
  • Dead Letter Queues catch failed messages for later reprocessing

Use Cases Demonstrated

  1. AI Message Processing: User submits text → Queue → AI generates response → Display in UI
  2. Image Caption Generation: User uploads image to R2 → Event notification → Queue → AI generates caption → Display in UI

Architecture

This monorepo contains two applications:

User Application

A TanStack Start frontend application (producer) that:

  • Provides an input form for sending messages to the queue
  • Uploads images to Cloudflare R2 storage
  • Polls KV store to display processed results
  • Runs on http://localhost:3000

Data Service

A Cloudflare Worker backend (consumer) that:

  • Consumes messages from the queue in batches
  • Routes events to appropriate handlers using type-safe Zod schemas
  • Processes messages with Cloudflare AI (Llama 2, LLaVA models)
  • Stores results in Cloudflare KV for retrieval

Shared Package

data-ops: Contains shared Zod schemas for type-safe queue messages

Setup

# Install dependencies and build shared packages
pnpm run setup

Prerequisites

You'll need to create these Cloudflare resources:

  1. Queue: queue-example (main processing queue)
  2. Dead Letter Queue: catch-all-queue (catches failed messages)
  3. R2 Bucket: queue-example with event notifications configured
  4. KV Namespace: CACHE (stores processed results)

Configure R2 event notifications to send PutObject events to queue-example queue.

Development

User Application (Producer)

pnpm run dev:user-application

Starts the TanStack Start application on port 3000.

Data Service (Consumer)

pnpm run dev:data-service

Starts the Cloudflare Worker locally (connects to production queue with remote: true).

Queue Configuration

Producer Configuration

apps/user-application/wrangler.jsonc:

"queues": {
  "producers": [
    {
      "binding": "QUEUE",
      "queue": "queue-example",
      "remote": true  // Connect to production queue in dev
    }
  ]
}

Consumer Configuration

apps/data-service/wrangler.jsonc:

"queues": {
  "consumers": [
    {
      "queue": "queue-example",
      "max_retries": 3,           // Retry failed messages 3 times
      "retry_delay": 2,           // Wait 2 seconds between retries
      "dead_letter_queue": "catch-all-queue",
      "max_batch_size": 4,        // Process up to 4 messages at once
      "max_batch_timeout": 1      // Wait 1 second to fill batch
    }
  ]
}

Message Types

The project uses discriminated unions for type-safe message routing:

Example Message

{
  type: "EXAMPLE_MESSAGE",
  id: "uuid",
  message: "What is the meaning of life?"
}

Processed by Llama 2 AI model, response stored in KV.

R2 Event

{
  type: "R2_EVENT",
  account: "...",
  bucket: "queue-example",
  action: "PutObject",
  object: {
    key: "image.jpg",
    size: 12345,
    eTag: "..."
  }
}

Processed by LLaVA image-to-text model, caption stored in KV.

Advanced Features

Delayed Messages

await env.QUEUE.send(message, { delaySeconds: 300 }); // 5 minute delay

Manual Acknowledgment

async queue(batch: MessageBatch) {
  for (const message of batch.messages) {
    try {
      await processMessage(message.body);
      message.ack(); // Acknowledge successful processing
    } catch (error) {
      message.retry(); // Retry this specific message
    }
  }
}

Batch Operations

async queue(batch: MessageBatch) {
  console.log(batch.queue); // Queue name
  batch.ackAll();   // Acknowledge all messages
  batch.retryAll(); // Retry all messages
}

Deployment

User Application

pnpm run deploy:user-application

Data Service

pnpm run deploy:data-service

Key Learnings

  • Cloudflare Queues only support one consumer per queue (but unlimited producers)
  • Use remote: true in wrangler config to test with production queues locally
  • Run pnpm wrangler types to generate TypeScript bindings for Cloudflare resources
  • Event subscriptions eliminate the need to update application code for common events
  • Dead letter queues are essential for handling persistent failures

Resources

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 95.2%
  • CSS 4.8%