Cloudflare Template
Step-by-step guide to creating and deploying a Quickback project on Cloudflare Workers with D1.
The Cloudflare template creates a production-ready backend running on Cloudflare Workers with D1 (SQLite at the edge), KV storage, and Better Auth.
cloudflare is a bare scaffold — multi-tenant orgs + auth + DB are wired in, but there are no example features. Add your own feature files under quickback/features/. If you want a working example to read first, use the todos template — same providers, but with a complete todos feature ready to extend.
Create the Project
The fastest path is start — interactive, scaffold + login + compile in one flow:
mkdir my-app && cd my-app
npx @quickback-dev/cli start
# pick "Cloudflare (bare scaffold)" at the promptScriptable equivalent:
quickback create cloudflare my-app # aliases: cf
cd my-appThis scaffolds a project with:
my-app/
├── quickback/
│ ├── quickback.config.ts # Compiler configuration
│ └── features/ # (empty — add your own)
├── src/ # Compiled output (generated by `quickback compile`)
├── package.json
├── tsconfig.json
├── wrangler.toml
└── drizzle.config.tsGenerated Configuration
quickback.config.ts
export default {
name: "my-app",
template: "hono",
features: {
organizations: true,
},
auth: {
// Use "role+" in access rules to mean "this role and above".
// e.g. roles: ["member+"] expands to ["member", "admin", "owner"].
roleHierarchy: ["member", "admin", "owner"],
},
providers: {
runtime: { name: "cloudflare", config: {} },
database: {
name: "cloudflare-d1",
config: { binding: "DB", realtime: true },
},
auth: { name: "better-auth", config: {} },
},
};Adding your first feature
Create a file at quickback/features/<name>/<name>.ts and export a defineTable(...) default. The shape used by the todos template makes a good starting point — see todos.ts in the template registry for a full multi-tenant example with masking, actions, and a named view.
// quickback/features/posts/posts.ts
import { sqliteTable, text } from "drizzle-orm/sqlite-core";
import { defineTable } from "@quickback/compiler";
export const posts = sqliteTable("posts", {
id: text("id").primaryKey(),
title: text("title").notNull(),
body: text("body"),
organizationId: text("organization_id").notNull(),
userId: text("user_id").notNull(),
});
export default defineTable(posts, {
firewall: { organization: {}, owner: {}, softDelete: {} },
guards: {
createable: ["title", "body"],
updatable: ["title", "body"],
immutable: ["id", "organizationId", "userId"],
},
read: { access: { roles: ["member+"] } },
create: { access: { roles: ["member+"] } },
update: { access: { roles: ["member+"] } },
delete: { access: { roles: ["admin+"] }, mode: "soft" },
});Setup Steps
If you used quickback start, dependencies, the first compile, and login are already done — skip to step 4. If you used quickback create, run these:
1. Install Dependencies
npm install2. Compile Your Definitions
quickback compileThis prompts for an account on the first run (sign in or create one in the browser), then generates the full src/ directory, migrations, and configuration files. Subsequent compiles re-use the stored session.
3. Create D1 Databases
The Cloudflare template uses dual database mode by default — separate databases for auth and application data.
# Create the auth database
npx wrangler d1 create my-app-auth
# Create the features database
npx wrangler d1 create my-app-featuresCopy the database IDs from the output and update your wrangler.toml:
[[d1_databases]]
binding = "AUTH_DB"
database_name = "my-app-auth"
database_id = "paste-auth-id-here"
[[d1_databases]]
binding = "DB"
database_name = "my-app-features"
database_id = "paste-features-id-here"4. Run Migrations (Local)
npm run db:migrate:localThis applies migrations to both databases locally.
5. Start Development Server
npm run devYour API is running at http://localhost:8787.
Wrangler Configuration
The compiler generates a wrangler.toml with all required bindings:
name = "my-app"
main = "src/index.ts"
compatibility_date = "2025-03-01"
compatibility_flags = ["nodejs_compat"]
[observability]
enabled = true
[placement]
mode = "smart"
# Auth database
[[d1_databases]]
binding = "AUTH_DB"
database_name = "my-app-auth"
database_id = "your-auth-db-id"
# Features database
[[d1_databases]]
binding = "DB"
database_name = "my-app-features"
database_id = "your-features-db-id"
# Key-value storage
[[kv_namespaces]]
binding = "KV"
id = "your-kv-id"Optional Bindings
Depending on your configuration, the compiler may also generate bindings for:
| Binding | Type | When Generated |
|---|---|---|
R2_BUCKET | R2 Bucket | fileStorage configured |
AI | Workers AI | embeddings configured on any feature |
VECTORIZE | Vectorize Index | embeddings configured |
EMBEDDINGS_QUEUE | Queue | embeddings configured |
WEBHOOKS_DB | D1 | Webhooks enabled |
WEBHOOKS_QUEUE | Queue | Webhooks enabled |
BROADCASTER | Service | realtime configured |
Dual Database Mode
By default, the Cloudflare template separates auth tables from application tables:
AUTH_DB— Better Auth tables (users,sessions,accounts,organizations,members)DB— Your feature tables (todos, etc.)
This keeps auth data isolated and allows independent scaling. Each database gets its own migration directory:
quickback/drizzle/
├── auth/ # Auth migrations
│ ├── meta/
│ └── 0000_*.sql
└── features/ # Feature migrations
├── meta/
└── 0000_*.sqlDeploying to Production
1. Apply Remote Migrations
npm run db:migrate:remote2. Deploy to Cloudflare
npm run deployThis runs wrangler deploy to push your worker to Cloudflare's edge network.
Available Scripts
| Script | Command | Description |
|---|---|---|
dev | wrangler dev | Start local dev server |
deploy | npm run db:migrate:remote && wrangler deploy | Deploy to production |
db:migrate:local | Runs auth + features migrations locally | Apply migrations to local D1 |
db:migrate:remote | Runs auth + features migrations remotely | Apply migrations to production D1 |
Next Steps
- Add a new feature — Define your own tables with security
- Configure access control — Set role-based permissions
- Add custom actions — Business logic beyond CRUD
- Environment variables — Configure secrets and bindings