@kardoe/quickback 0.5.14 → 0.5.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import pc from "picocolors";
12
12
  // Version injected at build time by scripts/inject-version.ts
13
- const CLI_VERSION = "0.5.14";
13
+ const CLI_VERSION = "0.5.15";
14
14
  function getPackageVersion() {
15
15
  return CLI_VERSION;
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kardoe/quickback",
3
- "version": "0.5.14",
3
+ "version": "0.5.15",
4
4
  "description": "CLI for Quickback - one-shot backend generator",
5
5
  "author": "Paul Stenhouse",
6
6
  "license": "MIT",
@@ -4,26 +4,40 @@ description: Quickback API engine documentation - use when working with Quickbac
4
4
  allowed-tools: Read, Grep, Glob
5
5
  ---
6
6
 
7
- # Quickback: Backend Compiler for Secure APIs
7
+ # Quickback
8
8
 
9
- Quickback transforms declarative TypeScript definitions into secure, production-ready APIs. You define your database schema and security rules in a single file using Drizzle ORM — Quickback compiles them into a deployable backend with authentication, role-based permissions, tenant isolation, and field-level security.
9
+ Quickback is two things:
10
+
11
+ 1. **Compiler** — Transforms declarative TypeScript definitions into secure, production-ready APIs
12
+ 2. **Stack** — A Supabase alternative running entirely on Cloudflare (D1, R2, KV, Durable Objects, Queues, Workers AI)
10
13
 
11
14
  The output is standard TypeScript (Hono, Drizzle, Better Auth) running on your own infrastructure. Not a managed platform — real code you own and control.
12
15
 
13
- ## How It Works
16
+ ## Accessing Documentation
17
+
18
+ The fastest way to look up detailed docs is via the CLI:
14
19
 
15
- 1. **Define** — Schema + security rules in TypeScript using `defineTable()`
16
- 2. **Compile** Quickback generates API routes, middleware, migrations, and RLS policies
17
- 3. **Deploy** Standard tooling to Cloudflare Workers, Supabase, or Neon
20
+ ```bash
21
+ quickback docs # List all available topics
22
+ quickback docs <topic> # Show docs for a specific topic
23
+ quickback docs firewall # Example: firewall docs
24
+ quickback docs cms/record-layouts # Example: CMS record layouts
25
+ ```
18
26
 
19
- ## Architecture
27
+ Full online docs: https://docs.quickback.dev
28
+
29
+ ---
30
+
31
+ # PART 1: THE COMPILER
32
+
33
+ The compiler takes your TypeScript definitions and generates API routes, middleware, migrations, typed SDKs, OpenAPI specs, and AI tool definitions.
20
34
 
21
35
  ```
22
36
  ┌─────────────────────────────────────────────────────────┐
23
37
  │ YOU DEFINE (in TypeScript) │
24
38
  │ • Drizzle schema (your data models) │
25
39
  │ • Security layers (Firewall, Access, Guards, Masking) │
26
- │ • Views (column-level projections)
40
+ │ • Views, Validation, Layouts
27
41
  │ • Actions (your business operations) │
28
42
  ├─────────────────────────────────────────────────────────┤
29
43
  │ QUICKBACK COMPILES TO │
@@ -35,24 +49,7 @@ The output is standard TypeScript (Hono, Drizzle, Better Auth) running on your o
35
49
  └─────────────────────────────────────────────────────────┘
36
50
  ```
37
51
 
38
- ## Four Security Layers
39
-
40
- Every API request passes through four layers in order:
41
-
42
- ```
43
- Request → Firewall → Access → Guards → Database → Masking → Response
44
- ```
45
-
46
- | Layer | Purpose | Failure |
47
- |-------|---------|---------|
48
- | **Firewall** | Tenant isolation via automatic WHERE clauses | 404 |
49
- | **Access** | Role-based CRUD permissions (deny by default) | 403 |
50
- | **Guards** | Field modification rules (protected/immutable fields) | 400 |
51
- | **Masking** | PII redaction for unauthorized viewers | — |
52
-
53
- ---
54
-
55
- # Project Structure
52
+ ## Project Structure
56
53
 
57
54
  ```
58
55
  my-app/
@@ -86,13 +83,9 @@ export default defineConfig({
86
83
 
87
84
  ## Compile Output and State Artifacts
88
85
 
89
- When a project has a `quickback/` folder and compiles runtime output to project root:
90
-
91
- - Runtime code stays in `build.outputDir` (for example `src/`, `wrangler.toml`, `package.json`).
92
- - Drizzle migration/state artifacts are written to `quickback/drizzle/...`.
93
- - Security contract report artifacts are written to `quickback/reports/...`.
94
-
95
- Treat `quickback/drizzle` as the canonical migration state location for repeated compiles.
86
+ - Runtime code stays in `build.outputDir` (e.g. `src/`, `wrangler.toml`, `package.json`)
87
+ - Drizzle migration/state artifacts are written to `quickback/drizzle/...`
88
+ - Security contract reports are written to `quickback/reports/...`
96
89
 
97
90
  ## Automatic Audit Fields
98
91
 
@@ -104,13 +97,13 @@ Quickback automatically injects these fields into every table:
104
97
 
105
98
  ---
106
99
 
107
- # Feature Definitions (Combined Mode)
100
+ ## Feature Definitions (Combined Mode)
108
101
 
109
102
  Features live in `quickback/features/{name}/` with one file per table using `defineTable()`.
110
103
 
111
104
  **Important**: Legacy mode (separate schema.ts + resource.ts) is no longer supported.
112
105
 
113
- ## Example: todos.ts
106
+ ### Example: todos.ts
114
107
 
115
108
  ```typescript
116
109
  import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
@@ -127,10 +120,9 @@ export const todos = sqliteTable("todos", {
127
120
 
128
121
  export default defineTable(todos, {
129
122
  firewall: {
130
- organization: {}, // Auto-detects 'organizationId' column
131
- owner: {}, // Auto-detects 'userId' column
123
+ organization: {},
124
+ owner: {},
132
125
  },
133
-
134
126
  crud: {
135
127
  list: { access: { roles: ["member", "admin"] } },
136
128
  get: { access: { roles: ["member", "admin"] } },
@@ -138,16 +130,13 @@ export default defineTable(todos, {
138
130
  update: { access: { roles: ["admin"] } },
139
131
  delete: { access: { roles: ["admin"] }, mode: "soft" },
140
132
  },
141
-
142
133
  guards: {
143
134
  createable: ["title", "description", "completed"],
144
135
  updatable: ["title", "description", "completed"],
145
136
  },
146
-
147
137
  masking: {
148
138
  userId: { type: "redact", show: { roles: ["admin"] } },
149
139
  },
150
-
151
140
  layouts: {
152
141
  default: {
153
142
  sections: [
@@ -159,12 +148,11 @@ export default defineTable(todos, {
159
148
  });
160
149
  ```
161
150
 
162
- ## Internal/Junction Tables (No API)
151
+ ### Internal/Junction Tables (No API)
163
152
 
164
153
  Tables without a `defineTable()` default export get no API routes:
165
154
 
166
155
  ```typescript
167
- // No default export = no API routes generated
168
156
  export const roomAmenities = sqliteTable('room_amenities', {
169
157
  roomId: text('room_id').notNull(),
170
158
  amenityId: text('amenity_id').notNull(),
@@ -173,9 +161,22 @@ export const roomAmenities = sqliteTable('room_amenities', {
173
161
 
174
162
  ---
175
163
 
176
- # Security Pillars
164
+ ## Four Security Layers
177
165
 
178
- ## 1. Firewall Data Isolation
166
+ Every API request passes through four layers in order:
167
+
168
+ ```
169
+ Request → Firewall → Access → Guards → Database → Masking → Response
170
+ ```
171
+
172
+ | Layer | Purpose | Failure |
173
+ |-------|---------|---------|
174
+ | **Firewall** | Tenant isolation via automatic WHERE clauses | 404 |
175
+ | **Access** | Role-based CRUD permissions (deny by default) | 403 |
176
+ | **Guards** | Field modification rules (protected/immutable fields) | 400 |
177
+ | **Masking** | PII redaction for unauthorized viewers | — |
178
+
179
+ ### 1. Firewall — Data Isolation
179
180
 
180
181
  Compiles WHERE clauses to isolate data by ownership. Auto-detects column names.
181
182
 
@@ -189,32 +190,25 @@ firewall: {
189
190
  }
190
191
  ```
191
192
 
192
- ### Auto-Detection
193
-
194
- Quickback finds columns by convention:
193
+ Auto-detection:
195
194
  - `organization` → looks for `organizationId` or `organization_id`
196
195
  - `owner` → looks for `userId` or `user_id`
197
196
  - `team` → looks for `teamId` or `team_id`
198
197
 
199
- ### Column Overrides
198
+ Column overrides:
200
199
 
201
200
  ```typescript
202
201
  firewall: {
203
202
  organization: {
204
- column: 'tenant_id', // Instead of 'organizationId'
205
- source: 'ctx.tenant.id', // Instead of 'ctx.activeOrgId'
203
+ column: 'tenant_id',
204
+ source: 'ctx.tenant.id',
206
205
  },
207
206
  }
208
207
  ```
209
208
 
210
- ### Owner Modes
211
-
212
- | Mode | Behavior |
213
- |------|----------|
214
- | (default) | Strict — only own records |
215
- | `optional` | Own records + records with NULL owner (admin pattern) |
209
+ Owner modes: default = strict (only own records), `optional` = own + NULL owner (admin pattern).
216
210
 
217
- ## 2. Access — Permission Checks
211
+ ### 2. Access — Permission Checks
218
212
 
219
213
  Role-based and record-based access control. Deny by default.
220
214
 
@@ -235,30 +229,11 @@ crud: {
235
229
  }
236
230
  ```
237
231
 
238
- ### Record Conditions
232
+ Record condition operators: `equals`, `notEquals`, `in`, `notIn`, `greaterThan`, `lessThan`
239
233
 
240
- | Operator | Example |
241
- |----------|---------|
242
- | `equals` | `{ status: { equals: 'active' } }` |
243
- | `notEquals` | `{ status: { notEquals: 'archived' } }` |
244
- | `in` | `{ status: { in: ['active', 'pending'] } }` |
245
- | `notIn` | `{ status: { notIn: ['deleted'] } }` |
246
- | `greaterThan` | `{ amount: { greaterThan: 0 } }` |
247
- | `lessThan` | `{ amount: { lessThan: 10000 } }` |
234
+ Context variables: `$ctx.userId`, `$ctx.activeOrgId`, `$ctx.activeTeamId`, `$ctx.roles`, `$ctx.isAnonymous`
248
235
 
249
- ### Context Variables
250
-
251
- | Variable | Type | Description |
252
- |----------|------|-------------|
253
- | `$ctx.userId` | string | Current user's ID |
254
- | `$ctx.activeOrgId` | string | Active organization ID |
255
- | `$ctx.activeTeamId` | string \| null | Active team ID |
256
- | `$ctx.roles` | string[] | User's roles in active org |
257
- | `$ctx.isAnonymous` | boolean | Anonymous user flag |
258
-
259
- ## 3. Guards — Field Protection
260
-
261
- Controls which fields can be modified.
236
+ ### 3. Guards — Field Protection
262
237
 
263
238
  ```typescript
264
239
  guards: {
@@ -269,168 +244,84 @@ guards: {
269
244
  }
270
245
  ```
271
246
 
272
- **Compile-time validation**:
273
- - Field can't be in both `createable` and `protected`
274
- - Field can't be in both `updatable` and `immutable`
275
- - All referenced fields must exist in the schema
276
-
277
- **System-managed fields** (always protected):
278
- - `createdAt`, `createdBy` — Set on INSERT
279
- - `modifiedAt`, `modifiedBy` — Set on INSERT/UPDATE
280
- - `deletedAt`, `deletedBy` — Set on soft DELETE
247
+ System-managed fields (always protected): `createdAt`, `createdBy`, `modifiedAt`, `modifiedBy`, `deletedAt`, `deletedBy`
281
248
 
282
- ### PUT / Upsert
249
+ PUT/Upsert requires both `generateId: false` and `guards: false`.
283
250
 
284
- PUT is only available when BOTH conditions are met:
285
- - `generateId: false` in database config
286
- - `guards: false` (disables field protection)
287
-
288
- ```typescript
289
- export default defineTable(external_orders, {
290
- guards: false,
291
- crud: {
292
- put: { access: { roles: ['sync-service'] } },
293
- get: {},
294
- list: {},
295
- },
296
- });
297
- ```
298
-
299
- ## 4. Masking — PII Redaction
251
+ ### 4. Masking PII Redaction
300
252
 
301
253
  ```typescript
302
254
  masking: {
303
- ssn: {
304
- type: 'ssn', // *****6789
305
- show: { roles: ['admin', 'hr'] },
306
- },
307
- email: {
308
- type: 'email', // p***@e******.com
309
- show: { roles: ['admin'], or: 'owner' },
310
- },
255
+ ssn: { type: 'ssn', show: { roles: ['admin', 'hr'] } },
256
+ email: { type: 'email', show: { roles: ['admin'], or: 'owner' } },
311
257
  }
312
258
  ```
313
259
 
314
- ### Mask Types
315
-
316
- | Type | Output |
317
- |------|--------|
318
- | `email` | `p***@e******.com` |
319
- | `phone` | `******4567` |
320
- | `ssn` | `*****6789` |
321
- | `creditCard` | `************1234` |
322
- | `name` | `J*** D**` |
323
- | `redact` | `[REDACTED]` |
324
- | `custom` | Your function |
325
-
326
- ### Auto-Detection
260
+ Mask types: `email`, `phone`, `ssn`, `creditCard`, `name`, `redact`, `custom`
327
261
 
328
- The compiler warns if sensitive columns aren't explicitly configured:
329
-
330
- | Column Pattern | Default Mask |
331
- |---------------|-------------|
332
- | `email` | `email` |
333
- | `phone`, `mobile`, `fax` | `phone` |
334
- | `ssn`, `socialsecurity` | `ssn` |
335
- | `creditcard`, `cc` | `creditCard` |
336
- | `password`, `secret`, `token` | `redact` |
262
+ Auto-detection warns on unmasked sensitive columns (`email`, `phone`, `ssn`, `password`, etc.).
337
263
 
338
264
  ---
339
265
 
340
- # Views — Column-Level Security
266
+ ## Views — Column-Level Security
341
267
 
342
- Views are named projections that control which fields are visible based on role.
268
+ Named projections that control which fields are visible based on role.
343
269
 
344
270
  ```typescript
345
- export default defineTable(customers, {
346
- // ... firewall, guards, etc.
347
-
348
- views: {
349
- summary: {
350
- fields: ['id', 'name', 'email'],
351
- access: { roles: ['member', 'admin'] },
352
- },
353
- full: {
354
- fields: ['id', 'name', 'email', 'phone', 'ssn', 'address'],
355
- access: { roles: ['admin'] },
356
- },
271
+ views: {
272
+ summary: {
273
+ fields: ['id', 'name', 'email'],
274
+ access: { roles: ['member', 'admin'] },
357
275
  },
358
- });
276
+ full: {
277
+ fields: ['id', 'name', 'email', 'phone', 'ssn', 'address'],
278
+ access: { roles: ['admin'] },
279
+ },
280
+ }
359
281
  ```
360
282
 
361
- **Generated endpoints**:
362
- - `GET /api/v1/customers/views/summary` — returns only id, name, email
363
- - `GET /api/v1/customers/views/full` — returns all fields (admin only)
364
-
365
- Views support the same query parameters as list (filtering, sorting, pagination). Masking still applies to returned fields.
283
+ Generated endpoints: `GET /api/v1/{resource}/views/{viewName}`
366
284
 
367
285
  ---
368
286
 
369
- # Validation
370
-
371
- Field-level validation rules compiled into the API:
287
+ ## Validation
372
288
 
373
289
  ```typescript
374
- export default defineTable(rooms, {
375
- // ... other config
376
-
377
- validation: {
378
- name: { minLength: 1, maxLength: 100 },
379
- capacity: { min: 1, max: 1000 },
380
- roomType: { enum: ['meeting', 'conference', 'breakout'] },
381
- email: { email: true },
382
- website: { url: true },
383
- code: { pattern: '^[A-Z]{3}$' },
384
- },
385
- });
290
+ validation: {
291
+ name: { minLength: 1, maxLength: 100 },
292
+ capacity: { min: 1, max: 1000 },
293
+ roomType: { enum: ['meeting', 'conference', 'breakout'] },
294
+ email: { email: true },
295
+ code: { pattern: '^[A-Z]{3}$' },
296
+ }
386
297
  ```
387
298
 
388
299
  ---
389
300
 
390
- # CMS Record Layouts
301
+ ## CMS Record Layouts
391
302
 
392
- Control how fields are grouped on the CMS record detail page. Without layouts, fields are auto-grouped by naming heuristics.
303
+ Control how fields are grouped on the CMS record detail page.
393
304
 
394
305
  ```typescript
395
- export default defineTable(contacts, {
396
- // ... firewall, crud, guards, etc.
397
-
398
- layouts: {
399
- default: {
400
- sections: [
401
- { label: "Contact Info", columns: 2, fields: ["name", "email", "phone", "mobile"] },
402
- { label: "Address", columns: 2, fields: ["address1", "address2", "city", "state", "zip"] },
403
- { label: "Internal Notes", collapsed: true, fields: ["notes", "internalNotes"] },
404
- ],
405
- },
406
- compact: {
407
- sections: [
408
- { label: "Summary", fields: ["name", "status", "email"] },
409
- ],
410
- },
306
+ layouts: {
307
+ default: {
308
+ sections: [
309
+ { label: "Contact Info", columns: 2, fields: ["name", "email", "phone"] },
310
+ { label: "Notes", collapsed: true, fields: ["notes", "internalNotes"] },
311
+ ],
411
312
  },
412
- });
313
+ }
413
314
  ```
414
315
 
415
- ### Section Options
416
-
417
- | Property | Type | Default | Description |
418
- |----------|------|---------|-------------|
419
- | `label` | string | required | Section header text |
420
- | `fields` | string[] | required | Column names to display |
421
- | `columns` | `1 \| 2` | `1` | Number of columns for field layout |
422
- | `collapsed` | boolean | `false` | Whether the section starts collapsed |
423
-
424
- Fields not assigned to any section are collected into an "Other Fields" section. When multiple layouts are defined, a dropdown appears in the CMS record detail header.
316
+ Section options: `label` (string), `fields` (string[]), `columns` (1|2, default 1), `collapsed` (boolean, default false). Unassigned fields go to "Other Fields".
425
317
 
426
318
  ---
427
319
 
428
- # Actions — Custom Business Logic
320
+ ## Actions — Custom Business Logic
429
321
 
430
- Actions are custom API endpoints for business logic beyond CRUD. Defined in a separate `actions.ts` file using `defineActions()`.
322
+ Defined in a separate `actions.ts` file using `defineActions()`.
431
323
 
432
324
  ```typescript
433
- // quickback/features/todos/actions.ts
434
325
  import { todos } from './todos';
435
326
  import { defineActions } from '@quickback/compiler';
436
327
  import { z } from 'zod';
@@ -438,73 +329,33 @@ import { z } from 'zod';
438
329
  export default defineActions(todos, {
439
330
  complete: {
440
331
  description: "Mark todo as complete",
441
- input: z.object({
442
- completedAt: z.string().datetime().optional(),
443
- }),
444
- guard: {
445
- roles: ["member", "admin"],
446
- record: { completed: { equals: false } },
447
- },
332
+ input: z.object({ completedAt: z.string().datetime().optional() }),
333
+ guard: { roles: ["member", "admin"], record: { completed: { equals: false } } },
448
334
  execute: async ({ db, record, ctx, input }) => {
449
- // Inline handler
450
335
  await db.update(todos).set({ completed: true }).where(eq(todos.id, record.id));
451
336
  return { success: true };
452
337
  },
453
338
  sideEffects: "sync",
454
339
  },
455
-
456
340
  archive: {
457
341
  description: "Archive a todo",
458
342
  input: z.object({}),
459
343
  guard: { roles: ["admin"] },
460
- handler: "./handlers/archive", // OR: external file handler
344
+ handler: "./handlers/archive",
461
345
  },
462
346
  });
463
347
  ```
464
348
 
465
- **Generated Route**: `POST /api/v1/todos/:id/complete`
466
-
467
- ### Record-based vs Standalone Actions
468
-
469
- **Record-based** (default) — operates on a specific record via `:id`:
470
- ```typescript
471
- approve: {
472
- description: "Approve an invoice",
473
- input: z.object({ notes: z.string().optional() }),
474
- guard: { roles: ["admin"] },
475
- handler: "./handlers/approve",
476
- }
477
- // → POST /api/v1/invoices/:id/approve
478
- ```
479
-
480
- **Standalone** — custom endpoint not tied to a record:
481
- ```typescript
482
- chat: {
483
- standalone: true,
484
- path: "/chat",
485
- method: "POST",
486
- responseType: "stream",
487
- input: z.object({ message: z.string() }),
488
- guard: { roles: ["member"] },
489
- handler: "./handlers/chat",
490
- }
491
- // → POST /api/v1/todos/chat
492
- ```
493
-
494
- ### Action Options
349
+ **Record-based** (default): `POST /api/v1/{resource}/:id/{action}`
350
+ **Standalone**: `standalone: true`, custom `path` and `method`
495
351
 
496
- | Option | Values | Description |
497
- |--------|--------|-------------|
498
- | `standalone` | `true` | Not tied to a record |
499
- | `method` | `GET`, `POST`, `PUT`, `PATCH`, `DELETE` | HTTP method (default: POST) |
500
- | `responseType` | `json`, `stream`, `file` | Response format |
501
- | `sideEffects` | `sync`, `async`, `fire-and-forget` | Execution hint |
352
+ Action options: `standalone`, `method` (GET/POST/PUT/PATCH/DELETE), `responseType` (json/stream/file), `sideEffects` (sync/async/fire-and-forget)
502
353
 
503
354
  ---
504
355
 
505
- # API Reference
356
+ ## API Reference
506
357
 
507
- ## CRUD Operations
358
+ ### CRUD Operations
508
359
 
509
360
  | Method | Endpoint | Description |
510
361
  |--------|----------|-------------|
@@ -515,278 +366,247 @@ chat: {
515
366
  | `DELETE` | `/api/v1/{resource}/:id` | Delete record |
516
367
  | `PUT` | `/api/v1/{resource}/:id` | Upsert (if enabled) |
517
368
 
518
- ## Batch Operations
519
-
520
- | Method | Endpoint | Description |
521
- |--------|----------|-------------|
522
- | `POST` | `/api/v1/{resource}/batch` | Batch create |
523
- | `PATCH` | `/api/v1/{resource}/batch` | Batch update |
524
- | `DELETE` | `/api/v1/{resource}/batch` | Batch delete |
525
- | `PUT` | `/api/v1/{resource}/batch` | Batch upsert (if enabled) |
526
-
527
- ```json
528
- // Request
529
- { "records": [{ "title": "A" }, { "title": "B" }], "options": { "atomic": false } }
530
-
531
- // Response
532
- { "success": [/* ... */], "errors": [/* ... */], "meta": { "total": 2, "succeeded": 2, "failed": 0 } }
533
- ```
534
-
535
- ## Query Parameters
369
+ ### Batch Operations
536
370
 
537
- ### Pagination
371
+ `POST|PATCH|DELETE|PUT /api/v1/{resource}/batch` with `{ "records": [...], "options": { "atomic": false } }`
538
372
 
539
- | Param | Default | Max |
540
- |-------|---------|-----|
541
- | `limit` | 50 | 100 |
542
- | `offset` | 0 | — |
373
+ ### Query Parameters
543
374
 
544
- ### Filtering Operators
375
+ | Feature | Syntax |
376
+ |---------|--------|
377
+ | Pagination | `?limit=50&offset=0` (max 100) |
378
+ | Filter | `?status=active`, `?amount.gt=100`, `?status.in=draft,pending` |
379
+ | Sort | `?sort=status,-createdAt` (multi-sort, `-` = desc) |
380
+ | Fields | `?fields=id,name,status` |
381
+ | Total count | `?total=true` |
382
+ | Search | `?search=conference` (OR'd LIKE across text columns) |
545
383
 
546
- | Operator | Example | SQL |
547
- |----------|---------|-----|
548
- | (none) | `?status=active` | `status = 'active'` |
549
- | `.gt` | `?amount.gt=100` | `amount > 100` |
550
- | `.gte` | `?amount.gte=100` | `amount >= 100` |
551
- | `.lt` | `?amount.lt=1000` | `amount < 1000` |
552
- | `.lte` | `?amount.lte=1000` | `amount <= 1000` |
553
- | `.ne` | `?status.ne=cancelled` | `status != 'cancelled'` |
554
- | `.like` | `?title.like=urgent` | `title LIKE '%urgent%'` |
555
- | `.in` | `?status.in=draft,pending` | `status IN ('draft','pending')` |
384
+ Filter operators: `.gt`, `.gte`, `.lt`, `.lte`, `.ne`, `.like`, `.in`
556
385
 
557
- ### Sorting (Multi-Sort)
386
+ ### Response Format
558
387
 
559
- ```
560
- ?sort=status,-createdAt # Multi-sort: status asc, createdAt desc
561
- ?sort=-createdAt # Single field descending
562
- ?sort=createdAt&order=desc # Legacy format (still supported)
388
+ ```json
389
+ {
390
+ "data": [{ "id": "...", "title": "Todo 1" }],
391
+ "pagination": { "limit": 10, "offset": 0, "count": 2, "total": 42 }
392
+ }
563
393
  ```
564
394
 
565
- No prefix = ascending, `-` prefix = descending. When commas or `-` detected, `order` param is ignored.
395
+ ### Error Responses
566
396
 
567
- ### Field Selection
397
+ | Status | Layer | Meaning |
398
+ |--------|-------|---------|
399
+ | `401` | Auth | Invalid/expired session |
400
+ | `403` | Access | Insufficient permissions |
401
+ | `404` | Firewall | Record not found or outside scope |
402
+ | `400` | Guards | Invalid field modification |
568
403
 
569
- ```
570
- ?fields=id,name,status # Return only these columns (LIST + GET routes)
571
- ```
404
+ ---
572
405
 
573
- Invalid field names silently ignored. Falls back to all columns if none valid. Views define their own fields — `?fields` doesn't apply to view routes. Masking still applies to selected fields.
406
+ ## Database Dialects
574
407
 
575
- ### Total Count
408
+ | Stack | Import | Table Function |
409
+ |-------|--------|----------------|
410
+ | Cloudflare D1 / SQLite | `drizzle-orm/sqlite-core` | `sqliteTable` |
411
+ | Supabase / PostgreSQL | `drizzle-orm/pg-core` | `pgTable` |
412
+ | MySQL | `drizzle-orm/mysql-core` | `mysqlTable` |
576
413
 
577
- ```
578
- ?total=true # Include pagination.total (LIST + VIEW routes)
579
- ```
414
+ ---
580
415
 
581
- Opt-in extra `COUNT(*)` query. Returns total matching records across all pages.
416
+ # PART 2: THE QUICKBACK STACK
582
417
 
583
- ### Search
418
+ The Quickback Stack is a production-ready backend platform built entirely on Cloudflare's edge infrastructure. It's a Supabase alternative where everything runs on your own Cloudflare account — your data, your infrastructure, global edge performance.
584
419
 
585
420
  ```
586
- ?search=conference # Full-text search across text columns (LIST + VIEW routes)
421
+ ┌─────────────────────────────────────────────────────────┐
422
+ │ QUICKBACK STACK ON CLOUDFLARE │
423
+ │ │
424
+ │ Workers — API runtime (Hono) │
425
+ │ D1 — SQLite database at the edge │
426
+ │ R2 — S3-compatible file storage │
427
+ │ KV — Distributed key-value store │
428
+ │ Durable Objects — Realtime WebSockets │
429
+ │ Queues — Background job processing │
430
+ │ Workers AI — Embeddings & vector search │
431
+ │ Better Auth — Authentication & organizations │
432
+ └─────────────────────────────────────────────────────────┘
587
433
  ```
588
434
 
589
- OR'd LIKE across all `text()` schema columns (system columns excluded). Combine with filters: `?search=urgent&status=active`.
435
+ ## Authentication (Better Auth)
590
436
 
591
- ## Response Format
437
+ Built on Better Auth with multi-tenant organization support.
592
438
 
593
- ```json
594
- {
595
- "data": [{ "id": "...", "title": "Todo 1" }],
596
- "pagination": {
597
- "limit": 10,
598
- "offset": 0,
599
- "count": 2,
600
- "total": 42
601
- }
439
+ - Email/password, magic links, passkeys, email OTP
440
+ - Three org roles: `owner`, `admin`, `member`
441
+ - Session storage in KV namespace
442
+ - All auth routes at `/auth/v1/*`
443
+ - Extensible via Better Auth plugins
444
+
445
+ ```typescript
446
+ // Config
447
+ providers: {
448
+ auth: defineAuth("better-auth"),
602
449
  }
603
450
  ```
604
451
 
605
- `count` = records on this page. `total` = total matching records (only when `?total=true`).
452
+ Docs: `quickback docs stack/auth/using-auth` | https://docs.quickback.dev/stack/auth
606
453
 
607
- ---
454
+ ## Database (D1)
455
+
456
+ SQLite at the edge with zero configuration. Multi-database pattern for independent scaling:
608
457
 
609
- # Webhooks
458
+ | Binding | Purpose |
459
+ |---------|---------|
460
+ | `DB` | Application data (features) |
461
+ | `AUTH_DB` | Auth sessions & users |
462
+ | `FILES_DB` | File metadata |
463
+ | `WEBHOOKS_DB` | Webhook tracking |
610
464
 
611
- ## Configuration
465
+ - Drizzle ORM for type-safe queries
466
+ - Auto-generated migrations on compile
467
+ - Application-layer security (firewall, access, guards, masking)
468
+ - Local dev with `.wrangler/state/`
469
+ - Neon Postgres available as alternative
612
470
 
613
471
  ```typescript
614
- // quickback.config.ts
615
472
  providers: {
616
473
  database: defineDatabase("cloudflare-d1", {
617
474
  splitDatabases: true,
618
- webhooksBinding: "WEBHOOKS_DB",
619
475
  }),
620
476
  }
621
477
  ```
622
478
 
623
- ## Inbound Webhooks (Receiving)
479
+ Docs: `quickback docs stack/database/d1` | https://docs.quickback.dev/stack/database/d1
624
480
 
625
- ```typescript
626
- import { onWebhookEvent } from './lib/webhooks';
481
+ ## File Storage (R2)
627
482
 
628
- onWebhookEvent('stripe:checkout.session.completed', async (ctx) => {
629
- const { data, env } = ctx;
630
- await createSubscription({ ... });
631
- });
632
- ```
483
+ S3-compatible object storage with built-in access control.
633
484
 
634
- ## Outbound Webhooks (Sending)
485
+ - Two-worker architecture: API worker (upload/manage) + Files worker (serve)
486
+ - Bucket-scoped access policies (public, organization, user)
487
+ - File metadata tracked in `FILES_DB`
488
+ - Soft deletes with org-level tenant isolation
489
+ - API at `/storage/v1/*`
635
490
 
636
- ```typescript
637
- import { emitWebhookEvent } from './lib/webhooks';
491
+ Docs: `quickback docs stack/storage/r2` | https://docs.quickback.dev/stack/storage/r2
638
492
 
639
- await emitWebhookEvent(
640
- 'user.created',
641
- { id: user.id, email: user.email },
642
- { organizationId: orgId },
643
- env
644
- );
645
- ```
493
+ ## KV Storage
646
494
 
647
- ## Available Events
495
+ Distributed key-value store optimized for reads (300+ edge locations, sub-millisecond).
648
496
 
649
- | Event | Description |
650
- |-------|-------------|
651
- | `user.created/updated/deleted` | User lifecycle |
652
- | `subscription.created/updated/cancelled/renewed` | Subscription lifecycle |
653
- | `organization.created/updated/deleted` | Organization lifecycle |
654
- | `organization.member_added/removed` | Membership changes |
655
- | `file.uploaded/deleted` | File storage |
497
+ - Use cases: sessions, caching, rate limiting, feature flags
498
+ - Eventually consistent (60 second propagation)
499
+ - TTL/expiration support
656
500
 
657
- **Wildcards**: `user.*`, `subscription.*`, `organization.*`, `file.*`, `*`
501
+ Docs: `quickback docs stack/storage/kv` | https://docs.quickback.dev/stack/storage/kv
658
502
 
659
- ---
503
+ ## Realtime (Durable Objects)
660
504
 
661
- # CLI Commands
505
+ WebSocket connections for live updates via organization-scoped Durable Objects.
662
506
 
663
- ## Core Commands
507
+ - CRUD event broadcasting (insert, update, delete)
508
+ - Custom broadcasts and event namespaces
509
+ - Role-based filtering and per-role field masking
510
+ - Session token or API key authentication
511
+ - Type-safe with `defineRealtime()` for custom events
512
+ - Endpoint: `/realtime/v1/websocket`
664
513
 
665
- | Command | Description |
666
- |---------|-------------|
667
- | `quickback create <template> <name>` | Create project from template |
668
- | `quickback compile` | Compile definitions to output |
669
- | `quickback login` | Authenticate for Pro templates |
670
- | `quickback logout` | Clear stored credentials |
671
- | `quickback whoami` | Show current auth status |
514
+ Docs: `quickback docs stack/realtime/durable-objects` | https://docs.quickback.dev/stack/realtime
672
515
 
673
- ## Available Templates
516
+ ## Queues (Background Processing)
674
517
 
675
- | Template | Stack | Status |
676
- |----------|-------|--------|
677
- | `cloudflare` | Cloudflare Workers + D1 + Better Auth | Free |
678
- | `bun` | Bun + SQLite + Better Auth | Free |
679
- | `turso` | Turso/LibSQL + Better Auth | Pro |
518
+ Cloudflare Queues for reliable async job processing.
680
519
 
681
- ## Claude Code Integration
520
+ | Queue | Purpose |
521
+ |-------|---------|
522
+ | `EMBEDDINGS_QUEUE` | Auto-generate embeddings |
523
+ | `WEBHOOKS_QUEUE` | Webhook delivery |
524
+ | Custom queues | Your background jobs |
682
525
 
683
- | Command | Description |
684
- |---------|-------------|
685
- | `quickback claude install` | Interactive install of Claude Code skill |
686
- | `quickback claude install --global` | Install globally |
687
- | `quickback claude install --local` | Install for current project |
688
- | `quickback claude update` | Update to latest skill version |
689
- | `quickback claude remove` | Remove the skill |
690
- | `quickback claude status` | Show install status |
526
+ - Custom handlers via `defineQueue()` in `services/queues/`
527
+ - Auto-retry up to 3 times, max batch 10, 30s timeout
528
+ - Chaining between queues
691
529
 
692
- ---
530
+ Docs: `quickback docs stack/queues/using-queues` | https://docs.quickback.dev/stack/queues
693
531
 
694
- # Development Workflow
532
+ ## Vector & Embeddings (Workers AI)
695
533
 
696
- ```bash
697
- # 1. Create project
698
- quickback create cloudflare my-app
534
+ Auto-generated embeddings for similarity search and classification.
699
535
 
700
- # 2. Define features in quickback/features/
536
+ - Table-level config via `embeddings` in `defineTable()`
537
+ - Service-level config via `defineEmbedding()`
538
+ - Default model: `@cf/baai/bge-base-en-v1.5` (768 dimensions)
539
+ - Async processing via `EMBEDDINGS_QUEUE`
540
+ - Vector storage in D1 + optional Vectorize index
541
+ - API: `/api/v1/embeddings`
701
542
 
702
- # 3. Compile
703
- quickback compile
543
+ Docs: `quickback docs stack/vector/embeddings` | https://docs.quickback.dev/stack/vector
704
544
 
705
- # 4. Run
706
- cd dist
707
- npm install
708
- npm run db:generate
709
- npm run dev
710
- ```
711
-
712
- ---
713
-
714
- # Deployment Targets
545
+ ## Webhooks
715
546
 
716
- ## Cloudflare Stack (Full Edge)
547
+ ### Inbound (Receiving)
717
548
 
718
- Hono API on Workers, D1 database, Better Auth, R2 file storage, KV, Queues, Durable Objects for realtime.
549
+ Receive events from Stripe, Paddle, GitHub, etc. at `POST /webhooks/v1/inbound/:provider`
719
550
 
720
551
  ```typescript
721
- defineConfig({
722
- providers: {
723
- runtime: defineRuntime("cloudflare"),
724
- database: defineDatabase("cloudflare-d1"),
725
- auth: defineAuth("better-auth"),
726
- },
552
+ import { onWebhookEvent } from './lib/webhooks';
553
+
554
+ onWebhookEvent('stripe:checkout.session.completed', async (ctx) => {
555
+ const { data, env } = ctx;
556
+ await createSubscription({ ... });
727
557
  });
728
558
  ```
729
559
 
730
- ## Supabase (RLS Policies)
560
+ ### Outbound (Sending)
731
561
 
732
- Compiles Quickback definitions into Row Level Security policies for Postgres-level security.
562
+ Emit signed events when data changes. HMAC-SHA256 signing, async delivery via queue with exponential backoff retry.
733
563
 
734
564
  ```typescript
735
- defineConfig({
736
- providers: {
737
- runtime: defineRuntime("supabase"),
738
- database: defineDatabase("supabase"),
739
- auth: defineAuth("supabase"),
740
- },
741
- });
742
- ```
743
-
744
- ## Neon (Serverless Postgres)
745
-
746
- Serverless Postgres with compiled RLS policies and branching support.
565
+ import { emitWebhookEvent } from './lib/webhooks';
747
566
 
748
- ---
567
+ await emitWebhookEvent('user.created', { id: user.id, email: user.email }, { organizationId: orgId }, env);
568
+ ```
749
569
 
750
- # Error Responses
570
+ Event patterns: `user.*`, `subscription.*`, `organization.*`, `file.*`, `*`
571
+ Endpoint management: `/webhooks/v1/endpoints`
751
572
 
752
- | Status | Layer | Meaning |
753
- |--------|-------|---------|
754
- | `401` | Auth | Invalid/expired session |
755
- | `403` | Access | Insufficient permissions |
756
- | `404` | Firewall | Record not found or outside scope |
757
- | `400` | Guards | Invalid field modification |
758
- | `429` | Rate limit | Too many requests |
573
+ Docs: `quickback docs stack/webhooks/outbound` | https://docs.quickback.dev/stack/webhooks
759
574
 
760
575
  ---
761
576
 
762
- # Database Dialects
763
-
764
- Detect from `quickback.config.ts` and use the correct imports:
765
-
766
- | Stack | Import | Table Function |
767
- |-------|--------|----------------|
768
- | Cloudflare D1 / SQLite | `drizzle-orm/sqlite-core` | `sqliteTable` |
769
- | Supabase / PostgreSQL | `drizzle-orm/pg-core` | `pgTable` |
770
- | MySQL | `drizzle-orm/mysql-core` | `mysqlTable` |
577
+ # CLI Commands
771
578
 
772
- ---
579
+ | Command | Description |
580
+ |---------|-------------|
581
+ | `quickback create <template> <name>` | Create project from template |
582
+ | `quickback compile` | Compile definitions to output |
583
+ | `quickback docs [topic]` | Browse built-in documentation |
584
+ | `quickback login` | Authenticate for Pro templates |
585
+ | `quickback logout` | Clear stored credentials |
586
+ | `quickback whoami` | Show current auth status |
587
+ | `quickback claude install` | Install Claude Code skill |
588
+ | `quickback claude update` | Update to latest skill version |
773
589
 
774
- # Documentation
590
+ ## Available Templates
775
591
 
776
- ## CLI Docs (Recommended)
592
+ | Template | Stack | Status |
593
+ |----------|-------|--------|
594
+ | `cloudflare` | Cloudflare Workers + D1 + Better Auth | Free |
595
+ | `bun` | Bun + SQLite + Better Auth | Free |
596
+ | `turso` | Turso/LibSQL + Better Auth | Pro |
777
597
 
778
- The fastest way to access documentation is via the Quickback CLI. It bundles all docs offline:
598
+ ## Development Workflow
779
599
 
780
600
  ```bash
781
- quickback docs # List all available topics
782
- quickback docs <topic> # Show docs for a specific topic
783
- quickback docs firewall # Example: firewall docs
784
- quickback docs cms/record-layouts # Example: CMS record layouts
601
+ quickback create cloudflare my-app # 1. Create project
602
+ # Define features in quickback/features/
603
+ quickback compile # 2. Compile
604
+ cd dist && npm install && npm run dev # 3. Run
785
605
  ```
786
606
 
787
- ## Online Docs
607
+ ---
788
608
 
789
- Full documentation at https://docs.quickback.dev
609
+ # Online Documentation
790
610
 
791
611
  - [Getting Started](https://docs.quickback.dev/compiler/getting-started)
792
612
  - [Concepts](https://docs.quickback.dev/compiler/definitions/concepts)
@@ -796,11 +616,16 @@ Full documentation at https://docs.quickback.dev
796
616
  - [Guards](https://docs.quickback.dev/compiler/definitions/guards)
797
617
  - [Masking](https://docs.quickback.dev/compiler/definitions/masking)
798
618
  - [Views](https://docs.quickback.dev/compiler/definitions/views)
799
- - [Validation](https://docs.quickback.dev/compiler/definitions/validation)
800
619
  - [Actions](https://docs.quickback.dev/compiler/definitions/actions)
801
620
  - [CRUD API](https://docs.quickback.dev/compiler/using-the-api/crud)
802
621
  - [Query Parameters](https://docs.quickback.dev/compiler/using-the-api/query-params)
803
622
  - [CLI Reference](https://docs.quickback.dev/compiler/cloud-compiler/cli)
623
+ - [Stack Overview](https://docs.quickback.dev/stack)
624
+ - [D1 Database](https://docs.quickback.dev/stack/database/d1)
625
+ - [R2 Storage](https://docs.quickback.dev/stack/storage/r2)
626
+ - [Realtime](https://docs.quickback.dev/stack/realtime)
627
+ - [Queues](https://docs.quickback.dev/stack/queues)
628
+ - [Embeddings](https://docs.quickback.dev/stack/vector)
629
+ - [Webhooks](https://docs.quickback.dev/stack/webhooks)
804
630
  - [CMS Overview](https://docs.quickback.dev/cms)
805
631
  - [CMS Record Layouts](https://docs.quickback.dev/cms/record-layouts)
806
- - [CMS Schema Format](https://docs.quickback.dev/cms/schema-format)