Quickback Docs

Providers

Runtime, database, auth, and storage provider options for quickback.config.ts.

Providers configure which services your compiled backend targets.

Runtime Providers

ProviderDescription
cloudflareCloudflare Workers (Hono)
bunBun runtime (Hono)
nodeNode.js runtime (Hono)
import { defineRuntime } from "@quickback/compiler";

providers: {
  runtime: defineRuntime("cloudflare"),
}

Runtime Options

OptionTypeDescription
compatibilityDatestringEmits Cloudflare compatibility_date in wrangler.toml.
placement.mode"smart" | "standard"Emits Cloudflare's [placement] block.
observability.enabledbooleanEmits top-level Workers Logs persistence under [observability].
observability.headSamplingRatenumberTop-level Workers Logs sample rate from 0 to 1.
observability.logs.enabledbooleanEmits [observability.logs].
observability.logs.invocationLogsbooleanEnables or suppresses invocation logs.
observability.logs.destinationsstring[]Named log export destinations configured in Cloudflare.
observability.logs.headSamplingRatenumberWorkers Logs sample rate from 0 to 1.
observability.logs.persistbooleanKeeps exported logs in the Cloudflare dashboard.
observability.traces.enabledbooleanEmits [observability.traces].
observability.traces.destinationsstring[]Named trace export destinations configured in Cloudflare.
observability.traces.headSamplingRatenumberWorkers Traces sample rate from 0 to 1.
observability.traces.persistbooleanKeeps exported traces in the Cloudflare dashboard.
routes{ pattern, custom_domain? }[]Custom domain routes for the generated Worker.
providers: {
  runtime: defineRuntime("cloudflare", {
    compatibilityDate: "2026-05-13",
    placement: { mode: "smart" },
    observability: {
      enabled: true,
      logs: {
        enabled: true,
        invocationLogs: false,
        destinations: ["logs-prod"],
        headSamplingRate: 0.6,
      },
      traces: {
        enabled: true,
        destinations: ["otel-prod"],
        headSamplingRate: 0.05,
        persist: false,
      },
    },
  }),
}

Database Providers

ProviderDescription
cloudflare-d1Cloudflare D1 (SQLite)
better-sqlite3SQLite via better-sqlite3 (Bun/Node)
libsqlLibSQL / Turso
neonNeon (PostgreSQL)
supabaseSupabase (PostgreSQL)
import { defineDatabase } from "@quickback/compiler";

providers: {
  database: defineDatabase("cloudflare-d1", {
    generateId: "prefixed",          // "uuid" | "cuid" | "nanoid" | "prefixed" | "serial" | false
    namingConvention: "snake_case",   // "camelCase" | "snake_case"
    usePlurals: false,                // Auth table names: "users" vs "user"
  }),
}

Database Options

OptionTypeDefault (D1)Description
generateIdstring | false"prefixed"ID generation strategy
namingConventionstring"snake_case"Column naming convention
usePluralsbooleanfalsePluralize auth table names (e.g. user vs users)
splitDatabasesbooleantrueSeparate auth and features databases
authBindingstring"AUTH_DB"Binding name for auth database
featuresBindingstring"DB"Binding name for features database
databaseIdstringD1 database ID for the features database (used in generated wrangler.toml)
authDatabaseIdstringD1 database ID for the auth database
featuresDatabaseIdstringExplicit features DB ID (falls back to databaseId)
filesDatabaseIdstringD1 database ID for the files metadata database
webhooksDatabaseIdstringD1 database ID for the webhooks database
auditBindingstring"AUDIT_DB"Binding name for the security audit database
auditDatabaseNamestring"{app}-audit"D1 database name for audit events
auditDatabaseIdstringExplicit D1 database ID for audit events
experimentalRemote{ auth?, features?, audit?, webhooks?, files? }{}Per-binding experimental_remote = true flag for wrangler dev (see below)
realtimeboolean | { enabled?, wsTicket? }falseEnables the Broadcaster Durable Object and optional resource-scoped ws-ticket auth (see below)
webhookAdminRolesstring[]["owner", "admin"]Org roles allowed to manage webhook endpoints

Audit Database

Cross-tenant unsafe actions (see Actions → Unsafe mode) write mandatory audit events. Those events land in a dedicated D1 database — separate from your features DB — so audit history stays isolated from application data and can't be tampered with through a normal CRUD route.

You don't need to do anything to enable it — the compiler wires up the binding automatically if your project declares any unsafe action. To override defaults:

providers: {
  database: defineDatabase("cloudflare-d1", {
    auditBinding: "MY_AUDIT_DB",            // default: AUDIT_DB
    auditDatabaseName: "my-app-audit-prod", // default: {app}-audit
    auditDatabaseId: "c3b8866b-cc4f-4e51-8382-0af244584d93",
  }),
}

The binding name must match what your Worker reads at runtime. If you don't declare any unsafe actions, no audit DB is provisioned and these options have no effect.

Setting Database IDs for Deployment

When deploying to production, set your D1 database IDs so the compiler generates a ready-to-deploy wrangler.toml:

providers: {
  database: defineDatabase("cloudflare-d1", {
    authDatabaseId: "fe6c77c4-2131-40be-a8fc-3649eef2a38c",
    databaseId: "bba22981-cb6e-43d6-bf32-4193513c60b1",
    filesDatabaseId: "c3b8866b-cc4f-4e51-8382-0af244584d93",
  }),
}

If these are omitted, the compiler uses local-dev placeholders and you'll need to manually edit wrangler.toml before deploying.

Sharing deployed D1 in dev (experimental_remote)

Wrangler can read or write the deployed D1 for a specific binding while wrangler dev runs locally. A common workflow: share the production auth DB in dev so real sessions and users work against the running local Worker, while feature writes stay sandboxed in the local SQLite shadow.

Quickback regenerates wrangler.toml on every compile, so flags like experimental_remote = true need to live in quickback.config.ts:

providers: {
  database: defineDatabase("cloudflare-d1", {
    authDatabaseId: "fe6c77c4-2131-40be-a8fc-3649eef2a38c",
    experimentalRemote: { auth: true },
  }),
}

Keys map to logical bindings, not the runtime binding string:

KeyBinding affected
authAUTH_DB (also applied to the files worker's auth binding)
featuresDB (or the legacy single-DB binding when splitDatabases: false)
auditAUDIT_DB
webhooksWEBHOOKS_DB
filesFILES_DB (also applied to the files worker)

Only true flips the flag on; false or omitted leaves the binding local. Off by default — only enable against databases you intend to share.

experimental_remote is a Wrangler dev feature. A flagged binding hits the deployed D1 from wrangler dev, so any write goes to production. Only enable for bindings you've decided are safe to read/write from local dev.

Realtime Broadcast Config

Set realtime: true to enable the generated Broadcaster Durable Object with the default organization-scoped routing model:

providers: {
  database: defineDatabase("cloudflare-d1", {
    realtime: true,
  }),
}

For relationship-authorized resource scopes, use the object form:

providers: {
  database: defineDatabase("cloudflare-d1", {
    realtime: {
      wsTicket: {
        role: "attendee",
        requestField: "eventId",
        scopeTable: "events",
      },
    },
  }),
}

wsTicket tells the compiler how to authorize /broadcast/v1/ws-ticket and what scope to stamp into the short-lived websocket ticket:

  • role: an authz role declared under authz.roles that uses a direct relationship ({ via: "..." })
  • requestField: the JSON body field the client posts to /broadcast/v1/ws-ticket
  • scopeTable: the parent resource table that defines the broadcast scope key

In other words: the relationship table proves access, and the scopeTable row defines the channel. For an attendee flow, guests (or RSVP) would be the relationship evidence and events would be the scopeTable.

Quickback keeps Better Auth as the front door, then mints its own 60-second websocket ticket after the authz check succeeds. The generated createRealtime() helper and Broadcaster share the same targeting model:

  • string target: historical organizationId shorthand
  • object target: { scopeKey?, organizationId?, userId? }

See Realtime for the client-side connection flow and helper examples.

usePlurals only affects auth tables generated by Better Auth (e.g. user vs users, session vs sessions). Feature table names come directly from your Drizzle schema definitions — whatever you pass to sqliteTable() or pgTable() is used as-is. The namingConvention setting applies to both auth and feature column names.

ID Generation Options

ValueDescriptionExample
"uuid"Server generates UUID550e8400-e29b-41d4-a716-446655440000
"cuid"Server generates CUIDclh2v8k9g0000l508h5gx8j1a
"nanoid"Server generates nanoid (21-char, URL-safe)V1StGXR8_Z5jdHi6B-myT
"short"6-char alphanumeric (nanoid customAlphabet)a3F9xK
"prefixed"Prefixed ID from table nameroom_abc123
"serial"Database auto-increments1, 2, 3
falseClient provides ID (enables PUT/upsert)Any string

"short" produces IDs with ~56.8 billion possible values (62^6). That's fine for small/scoped tables like rooms, invites, or share codes — but at roughly 238,000 rows you hit a 50% chance of collision (birthday bound). Don't use it for high-volume records. "nanoid" and "short" both require the nanoid package in your project.

Auth Providers

ProviderDescription
better-authBetter Auth with plugins
supabase-authSupabase Auth
externalExternal auth via Cloudflare service binding
import { defineAuth } from "@quickback/compiler";

providers: {
  auth: defineAuth("better-auth", {
    session: {
      expiresInDays: 7,
      updateAgeInDays: 1,
    },
    rateLimit: {
      enabled: true,
      window: 60,
      max: 100,
    },
  }),
}

Better Auth Options

OptionTypeDescription
session.expiresInDaysnumberSession expiration in days
session.updateAgeInDaysnumberSession refresh interval in days
rateLimit.enabledbooleanEnable rate limiting
rateLimit.windownumberRate limit window in seconds
rateLimit.maxnumberMax requests per window
socialProvidersobjectSocial login providers (google, github, discord)
debugLogsbooleanEnable auth debug logging

Auth table naming (usePlurals, namingConvention) is inherited from your database provider config — you don't need to set it separately on the auth provider. usePlurals controls whether auth tables are named user/session or users/sessions. It does not affect feature tables, which use whatever name you define in your schema.

Better Auth Plugins

When features: ["organizations"] is set in your config, the compiler automatically enables organization-related plugins. Additional plugins can be configured:

PluginDescription
organizationMulti-tenant organizations
adminAdmin panel access
apiKeyAPI key authentication
anonymousAnonymous sessions
upgradeAnonymousConvert anonymous to full accounts
twoFactorTwo-factor authentication
passkeyWebAuthn passkey login
magicLinkEmail magic link login
emailOtpEmail one-time password
deviceAuthorizationDevice auth flow (CLI tools)
jwtJWT token support
openAPIOpenAPI spec generation

Storage Providers

import { defineStorage, defineFileStorage } from "@quickback/compiler";

providers: {
  storage: defineStorage("kv", {
    binding: "KV_STORE",
    namespaceId: "7ed0f824a8c54c388713d9fdf63cd004",
  }),
  fileStorage: defineFileStorage("r2", {
    binding: "FILES",
    maxFileSize: "10mb",
    allowedTypes: ["image/png", "image/jpeg", "application/pdf"],
    publicDomain: "files.example.com",
  }),
}

The KV namespaceId can be set on the storage provider config, or as kvId on the auth provider config (since KV is primarily used for auth sessions). The compiler checks both locations.

Storage Types

TypeDescription
kvKey-value storage (Cloudflare KV, Redis)
r2Object storage (Cloudflare R2)
memoryIn-memory storage (development only)
redisRedis storage

File Storage Options

OptionTypeDescription
bindingstringR2 bucket binding name
maxFileSizestringMaximum file size (e.g. "10mb")
allowedTypesstring[]Allowed MIME types
publicDomainstringPublic domain for file URLs
userScopedBucketsbooleanScope files by user

On this page