# Cloudflare Template

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](/start/templates#multi-tenant-scaffolds) — 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:

```bash
mkdir my-app && cd my-app
npx @quickback-dev/cli start
# pick "Cloudflare (bare scaffold)" at the prompt
```

Scriptable equivalent:

```bash
quickback create cloudflare my-app    # aliases: cf
cd my-app
```

This 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.ts
```

## Generated Configuration

### quickback.config.ts

```typescript
export default {
  name: "my-app",
  template: "hono",
  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: {} },
  },
};
```

Multi-tenant organizations are on by default — no flag needed. To build a single-tenant app instead, set `organizations: false` on the database provider 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](/start/templates#multi-tenant-scaffolds) for a full multi-tenant example with masking, actions, and a named view.

```typescript
// 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(),
  // `ownerId` is the owner column the firewall auto-detects — it becomes
  // `WHERE owner_id = ctx.userId` and is auto-stamped on insert. Do not name
  // it `userId`: that name is not auto-detected on feature tables.
  ownerId: text("owner_id").notNull(),

  // Compiler-managed columns — the audit quartet on every table, plus the
  // soft-delete pair because `delete.mode` is "soft".
  createdAt: text("created_at").notNull().default('1970-01-01T00:00:00.000Z').$defaultFn(() => new Date().toISOString()),
  modifiedAt: text("modified_at").notNull().default('1970-01-01T00:00:00.000Z').$defaultFn(() => new Date().toISOString()).$onUpdate(() => new Date().toISOString()),
  createdBy: text("created_by"),
  modifiedBy: text("modified_by"),
  deletedAt: text("deleted_at"),
  deletedBy: text("deleted_by"),
});

export default defineTable(posts, {
  firewall: { organization: {}, owner: {}, softDelete: {} },
  guards: {
    createable: ["title", "body"],
    updatable: ["title", "body"],
    immutable: ["id", "organizationId", "ownerId"],
  },
  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

```bash
npm install
```

### 2. Compile Your Definitions

```bash
quickback compile
```

This 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.

```bash
# Create the auth database
npx wrangler d1 create my-app-auth

# Create the features database
npx wrangler d1 create my-app-features
```

Copy the database IDs from the output and update your `wrangler.toml`:

```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)

```bash
npm run db:migrate:local
```

This applies migrations to both databases locally.

### 5. Start Development Server

```bash
npm run dev
```

Your API is running at `http://localhost:8787`.

## Wrangler Configuration

The compiler generates a `wrangler.toml` with all required bindings:

```toml
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 | **managed** `fileStorage` — `{ managed: true }` (presign-only emits no binding) |
| `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 (`user`, `session`, `account`, `organization`, `member`)
- **`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_*.sql
```

## Deploying to Production

> [`quickback deploy`](/start/deploy) does all of this in one command — it provisions
> D1/KV/R2, writes the real ids back into your config, applies migrations, and ships the
> worker. The manual steps below are the escape hatch.


### 1. Apply Remote Migrations

```bash
npm run db:migrate:remote
```

### 2. Deploy to Cloudflare

```bash
npm run deploy
```

This 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/schema) — Define your own tables with security
- [Configure access control](/define/access) — Set role-based permissions
- [Add custom actions](/define/actions) — Business logic beyond CRUD
- [Environment variables](/configure/variables) — Configure secrets and bindings
