# Using Queues

This page covers the practical aspects of working with Cloudflare Queues in a Quickback project — publishing messages, configuring wrangler, and deploying.

## Publishing Messages

Send messages to a queue using the Cloudflare Queue binding in your action or route code:

```typescript
// In an action handler
export const execute: ActionExecutor = async ({ ctx, input, db }) => {
  await ctx.env.PROCESSING_QUEUE.send({
    type: "process_material",
    materialId: input.materialId,
    organizationId: ctx.activeOrgId,
  });

  return { queued: true };
};
```

The `type` field is required — the queue consumer uses it to dispatch messages to the correct handler.

### Sending to Built-in Queues

**Embeddings** are auto-enqueued by the compiler after CRUD operations. You can also manually enqueue:

```typescript
await ctx.env.EMBEDDINGS_QUEUE.send({
  type: "embedding",
  table: "claims",
  id: record.id,
  content: `${record.title} ${record.content}`,
  model: "@cf/baai/bge-base-en-v1.5",
  embeddingColumn: "embedding",
  metadata: { organizationId: ctx.activeOrgId },
  organizationId: ctx.activeOrgId,
});
```

**Webhooks** are enqueued via `emitWebhookEvent()` — see [Outbound Webhooks](/stack/webhooks/outbound).

## Chaining Queues

Queue handlers can publish to other queues for multi-stage pipelines:

```typescript
export default defineQueue({
  name: "ingest",
  messageType: "process_material",

  execute: async ({ message, db, env }) => {
    // Stage 1: Extract data
    const claims = await extractClaims(message.materialId, db);

    // Stage 2: Send each claim to next queue
    for (const claim of claims) {
      await env.ANALYSIS_QUEUE.send({
        type: "analyze_claim",
        claimId: claim.id,
      });
    }

    return { success: true };
  },
});
```

## Wrangler Configuration

Queue bindings are auto-generated in `wrangler.toml`. Here's what the compiler produces:

```toml
# Embeddings queue (auto-configured when embeddings enabled)
[[queues.producers]]
queue = "my-app-embeddings-queue"
binding = "EMBEDDINGS_QUEUE"

[[queues.consumers]]
queue = "my-app-embeddings-queue"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 3

# Webhooks queue (auto-configured when webhooks enabled)
[[queues.producers]]
queue = "my-app-webhooks-queue"
binding = "WEBHOOKS_QUEUE"

[[queues.consumers]]
queue = "my-app-webhooks-queue"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 3
dead_letter_queue = "my-app-webhooks-dlq"

# Custom queue (from additionalQueues config)
[[queues.producers]]
queue = "my-app-processing-queue"
binding = "PROCESSING_QUEUE"

[[queues.consumers]]
queue = "my-app-processing-queue"
max_batch_size = 5
max_batch_timeout = 60
max_retries = 5
```

## Generated Queue Consumer

The compiler generates a unified `src/queue-consumer.ts` that handles all message types:

```typescript
// src/queue-consumer.ts (generated)
export const queue = async (
  batch: MessageBatch<any>,
  env: CloudflareBindings
): Promise<void> => {
  const messageType = batch.messages[0]?.body?.type;

  switch (messageType) {
    case "embedding":
      await embeddingQueueHandler(batch, env);
      break;
    case "process_material":
      await ingestQueueHandler(batch, env);
      break;
    default:
      console.warn("[Queue] Unknown message type:", messageType);
      for (const msg of batch.messages) msg.ack();
  }
};
```

This consumer is exported alongside the `fetch` handler in the Workers entry point.

## Deployment

### 1. Create Queues

Before deploying, create the queues in Cloudflare:

```bash
wrangler queues create my-app-embeddings-queue
wrangler queues create my-app-processing-queue
```

### 2. Compile and Deploy

```bash
quickback compile
wrangler deploy
```

The single worker handles both HTTP requests and queue consumption.

### 3. Verify

Check queue status:

```bash
wrangler queues list
```

## Error Handling

The queue consumer handles errors per-message:

- **Return `{ success: true }`** — Message is acknowledged
- **Return `{ success: false, error: "..." }`** — Message is retried
- **Throw an exception** — Message is retried

After `max_retries` (default 3), failed messages are dropped or sent to a dead-letter queue if configured.

## Monitoring

Queue metrics are available in the Cloudflare dashboard:

- Messages enqueued / processed
- Retry count
- Consumer lag
- Dead-letter queue depth

## Cloudflare Only

Queues require the Cloudflare runtime. They are not available with the Bun or Node runtimes.

## See Also

- [Custom Queue Handlers](/stack/queues/handlers) — `defineQueue()` API and handler context
- [Automatic Embeddings](/stack/vector/embeddings) — How embedding jobs are enqueued
- [Outbound Webhooks](/stack/webhooks/outbound) — How webhook deliveries are enqueued
