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.
Create the Project
quickback create cloudflare my-app
cd my-appAliases: quickback create cf my-app
This scaffolds a project with:
my-app/
├── quickback/
│ ├── quickback.config.ts # Compiler configuration
│ └── features/
│ └── todos/
│ ├── todos.ts # Schema + security (defineTable)
│ └── actions.ts # Custom actions
├── src/ # Compiled output (generated)
├── drizzle/ # Migrations (generated)
├── package.json
├── tsconfig.json
├── wrangler.toml
└── drizzle.config.tsGenerated Configuration
quickback.config.ts
import { defineConfig } from "@quickback/define";
export default defineConfig({
name: "my-app",
template: "hono",
features: ["organizations"],
providers: {
runtime: { name: "cloudflare", config: {} },
database: {
name: "cloudflare-d1",
config: { binding: "DB" },
},
auth: { name: "better-auth", config: {} },
},
});Example Feature (todos.ts)
The template includes a todos feature with all four security pillars configured:
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
import { defineTable } from "@quickback/compiler";
export const todos = sqliteTable("todos", {
id: text("id").primaryKey(),
title: text("title").notNull(),
description: text("description"),
completed: integer("completed", { mode: "boolean" }).default(false),
priority: text("priority").default("medium"),
dueDate: integer("due_date", { mode: "timestamp" }),
organizationId: text("organization_id").notNull(),
ownerId: text("owner_id").notNull(),
});
export default defineTable(todos, {
firewall: {
organization: {},
owner: { mode: "optional" },
softDelete: {},
},
guards: {
createable: ["title", "description", "completed", "priority", "dueDate"],
updatable: ["title", "description", "completed", "priority", "dueDate"],
immutable: ["id", "organizationId", "ownerId"],
},
masking: {
description: {
type: "redact",
show: { roles: ["owner", "admin"] },
},
},
crud: {
list: { access: { roles: ["owner", "admin", "member"] } },
get: { access: { roles: ["owner", "admin", "member"] } },
create: { access: { roles: ["owner", "admin", "member"] } },
update: { access: { roles: ["owner", "admin", "member"] } },
delete: { access: { roles: ["owner", "admin"] }, mode: "soft" },
},
});Setup Steps
1. Install Dependencies
npm install2. Log In to the Compiler
quickback login3. Compile Your Definitions
quickback compileThis sends your definitions to the Quickback compiler and generates the full src/ directory, migrations, and configuration files.
4. 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"5. Run Migrations (Local)
npm run db:migrate:localThis applies migrations to both databases locally.
6. 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:
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