Quickback Docs

Custom Bindings

Declare extra Cloudflare bindings (send_email, r2_buckets, queues, services, durable_objects, vars, ai) in quickback.config.ts so they survive every compile.

Quickback's compiler owns the D1/KV/R2/ASSETS/queue blocks it needs for its own features. Anything else your worker binds to — Cloudflare Email, an extra R2 bucket, a custom queue, a service binding, your own Durable Object, Workers AI, or non-secret [vars] — goes under the top-level bindings key:

import { defineConfig, defineRuntime, defineDatabase, defineAuth } from "@kardoe/quickback";

export default defineConfig({
  name: "studio-mail",
  template: "hono",
  providers: {
    runtime: defineRuntime("cloudflare"),
    database: defineDatabase("cloudflare-d1", { splitDatabases: true }),
    auth: defineAuth("better-auth"),
  },
  bindings: {
    sendEmail: [{ name: "EMAIL" }],
    r2Buckets: [
      { binding: "MAIL_RAW",         bucketName: "studio-mail-raw" },
      { binding: "MAIL_ATTACHMENTS", bucketName: "studio-mail-attachments" },
    ],
    vars: {
      MAIL_SENDING_DOMAIN: "studio.example.com",
    },
  },
});

camelCase field names map 1:1 to the snake_case TOML blocks in wrangler.toml. Every compile regenerates wrangler.toml from your config, so these entries are the source of truth — no more post-compile patch scripts.

Fields

sendEmail

Emits [[send_email]]. Use when your worker calls env.<NAME>.send(...) via Cloudflare Email Service.

sendEmail: [
  {
    name: "EMAIL",
    // Optional: restrict who the worker is allowed to send to.
    destinationAddress: "alerts@example.com",
    allowedDestinationAddresses: ["alerts@example.com", "ops@example.com"],
  },
]

r2Buckets

Emits additional [[r2_buckets]] blocks alongside the compiler-owned files bucket.

r2Buckets: [
  {
    binding: "MAIL_RAW",
    bucketName: "studio-mail-raw",
    previewBucketName: "studio-mail-raw-preview", // optional
    jurisdiction: "eu",                           // optional
  },
]

queueProducers / queueConsumers

Emits [[queues.producers]] and [[queues.consumers]]. Useful for app-owned queues beyond the compiler's webhook/embedding queues.

queueProducers: [
  { binding: "MAIL_QUEUE", queue: "mail-send" },
],
queueConsumers: [
  {
    queue: "mail-send",
    maxBatchSize: 5,
    maxBatchTimeout: 30,
    maxRetries: 3,
    maxConcurrency: 2,
    deadLetterQueue: "mail-dlq",
  },
]

services

Emits [[services]] — service bindings to sibling workers.

services: [
  { binding: "INBOUND", service: "studio-mail-inbound", environment: "production" },
]

durableObjects

Emits [[durable_objects.bindings]] for app-owned Durable Objects. Don't use this for Quickback's built-in BROADCASTER realtime DO — that one is compiler-owned.

durableObjects: [
  { name: "SESSION_ROOM", className: "SessionRoom" },
  // Cross-worker DO: reference the exporting worker explicitly.
  { name: "RATE_LIMITER", className: "RateLimiter", scriptName: "rate-limit-worker" },
]

vars

Merged into the compiler's own [vars] table — emitting a second [vars] block would be invalid TOML. Values must be string, number, or boolean.

vars: {
  MAIL_SENDING_DOMAIN: "studio.example.com",
  MAIL_MAX_ATTACHMENTS: 10,
  MAIL_DEBUG: false,
}

Secrets do not go here. Use Wrangler's secret store instead:

wrangler secret put MAIL_INBOUND_SECRET

ai

Emits a top-level [ai] block. Use this if you need Workers AI bound under a name different from Quickback's internal AI.

ai: { binding: "USER_AI" }

Reserved binding names

Binding names that clash with compiler-owned bindings are rejected at validation time, with the offending field path in the error message. Reserved names depend on what your config enables:

NameReserved when
AUTH_DB, DBsplitDatabases: true (default)
DATABASEsplitDatabases: false
AUDIT_DBalways (cross-tenant unsafe-action audit)
WEBHOOKS_DB, WEBHOOKS_QUEUEa webhooks binding is configured
KValways
R2_BUCKET, FILES_DBfileStorage: defineFileStorage("cloudflare-r2")
VECTORIZEa Vectorize index is configured
BROADCASTERrealtime is enabled
EMAILemail: { provider: "cloudflare" }
ASSETScms: true or account: true
AI, EMBEDDINGS_QUEUEembeddings are configured

Pick a different name — the generated env on the handler side takes whatever binding you pick.

See also

On this page