Providers
Runtime, database, auth, and storage provider options for quickback.config.ts.
Providers configure which services your compiled backend targets.
Runtime Providers
| Provider | Description |
|---|---|
cloudflare | Cloudflare Workers (Hono) |
bun | Bun runtime (Hono) |
node | Node.js runtime (Hono) |
import { defineRuntime } from "@quickback/compiler";
providers: {
runtime: defineRuntime("cloudflare"),
}Runtime Options
| Option | Type | Description |
|---|---|---|
compatibilityDate | string | Emits Cloudflare compatibility_date in wrangler.toml. |
placement.mode | "smart" | "standard" | Emits Cloudflare's [placement] block. |
observability.enabled | boolean | Emits top-level Workers Logs persistence under [observability]. |
observability.headSamplingRate | number | Top-level Workers Logs sample rate from 0 to 1. |
observability.logs.enabled | boolean | Emits [observability.logs]. |
observability.logs.invocationLogs | boolean | Enables or suppresses invocation logs. |
observability.logs.destinations | string[] | Named log export destinations configured in Cloudflare. |
observability.logs.headSamplingRate | number | Workers Logs sample rate from 0 to 1. |
observability.logs.persist | boolean | Keeps exported logs in the Cloudflare dashboard. |
observability.traces.enabled | boolean | Emits [observability.traces]. |
observability.traces.destinations | string[] | Named trace export destinations configured in Cloudflare. |
observability.traces.headSamplingRate | number | Workers Traces sample rate from 0 to 1. |
observability.traces.persist | boolean | Keeps 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
| Provider | Description |
|---|---|
cloudflare-d1 | Cloudflare D1 (SQLite) |
better-sqlite3 | SQLite via better-sqlite3 (Bun/Node) |
libsql | LibSQL / Turso |
neon | Neon (PostgreSQL) |
supabase | Supabase (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
| Option | Type | Default (D1) | Description |
|---|---|---|---|
generateId | string | false | "prefixed" | ID generation strategy |
namingConvention | string | "snake_case" | Column naming convention |
usePlurals | boolean | false | Pluralize auth table names (e.g. user vs users) |
splitDatabases | boolean | true | Separate auth and features databases |
authBinding | string | "AUTH_DB" | Binding name for auth database |
featuresBinding | string | "DB" | Binding name for features database |
databaseId | string | — | D1 database ID for the features database (used in generated wrangler.toml) |
authDatabaseId | string | — | D1 database ID for the auth database |
featuresDatabaseId | string | — | Explicit features DB ID (falls back to databaseId) |
filesDatabaseId | string | — | D1 database ID for the files metadata database |
webhooksDatabaseId | string | — | D1 database ID for the webhooks database |
auditBinding | string | "AUDIT_DB" | Binding name for the security audit database |
auditDatabaseName | string | "{app}-audit" | D1 database name for audit events |
auditDatabaseId | string | — | Explicit D1 database ID for audit events |
experimentalRemote | { auth?, features?, audit?, webhooks?, files? } | {} | Per-binding experimental_remote = true flag for wrangler dev (see below) |
realtime | boolean | { enabled?, wsTicket? } | false | Enables the Broadcaster Durable Object and optional resource-scoped ws-ticket auth (see below) |
webhookAdminRoles | string[] | ["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:
| Key | Binding affected |
|---|---|
auth | AUTH_DB (also applied to the files worker's auth binding) |
features | DB (or the legacy single-DB binding when splitDatabases: false) |
audit | AUDIT_DB |
webhooks | WEBHOOKS_DB |
files | FILES_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 underauthz.rolesthat uses a direct relationship ({ via: "..." })requestField: the JSON body field the client posts to/broadcast/v1/ws-ticketscopeTable: 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
organizationIdshorthand - 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
| Value | Description | Example |
|---|---|---|
"uuid" | Server generates UUID | 550e8400-e29b-41d4-a716-446655440000 |
"cuid" | Server generates CUID | clh2v8k9g0000l508h5gx8j1a |
"nanoid" | Server generates nanoid (21-char, URL-safe) | V1StGXR8_Z5jdHi6B-myT |
"short" | 6-char alphanumeric (nanoid customAlphabet) | a3F9xK |
"prefixed" | Prefixed ID from table name | room_abc123 |
"serial" | Database auto-increments | 1, 2, 3 |
false | Client 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
| Provider | Description |
|---|---|
better-auth | Better Auth with plugins |
supabase-auth | Supabase Auth |
external | External 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
| Option | Type | Description |
|---|---|---|
session.expiresInDays | number | Session expiration in days |
session.updateAgeInDays | number | Session refresh interval in days |
rateLimit.enabled | boolean | Enable rate limiting |
rateLimit.window | number | Rate limit window in seconds |
rateLimit.max | number | Max requests per window |
socialProviders | object | Social login providers (google, github, discord) |
debugLogs | boolean | Enable 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:
| Plugin | Description |
|---|---|
organization | Multi-tenant organizations |
admin | Admin panel access |
apiKey | API key authentication |
anonymous | Anonymous sessions |
upgradeAnonymous | Convert anonymous to full accounts |
twoFactor | Two-factor authentication |
passkey | WebAuthn passkey login |
magicLink | Email magic link login |
emailOtp | Email one-time password |
deviceAuthorization | Device auth flow (CLI tools) |
jwt | JWT token support |
openAPI | OpenAPI 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
| Type | Description |
|---|---|
kv | Key-value storage (Cloudflare KV, Redis) |
r2 | Object storage (Cloudflare R2) |
memory | In-memory storage (development only) |
redis | Redis storage |
File Storage Options
| Option | Type | Description |
|---|---|---|
binding | string | R2 bucket binding name |
maxFileSize | string | Maximum file size (e.g. "10mb") |
allowedTypes | string[] | Allowed MIME types |
publicDomain | string | Public domain for file URLs |
userScopedBuckets | boolean | Scope files by user |