@kardoe/quickback 0.5.14 → 0.5.16

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.
@@ -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,11 @@ Quickback automatically injects these fields into every table:
104
97
 
105
98
  ---
106
99
 
107
- # Feature Definitions (Combined Mode)
100
+ ## Feature Definitions
108
101
 
109
102
  Features live in `quickback/features/{name}/` with one file per table using `defineTable()`.
110
103
 
111
- **Important**: Legacy mode (separate schema.ts + resource.ts) is no longer supported.
112
-
113
- ## Example: todos.ts
104
+ ### Example: todos.ts
114
105
 
115
106
  ```typescript
116
107
  import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
@@ -127,10 +118,9 @@ export const todos = sqliteTable("todos", {
127
118
 
128
119
  export default defineTable(todos, {
129
120
  firewall: {
130
- organization: {}, // Auto-detects 'organizationId' column
131
- owner: {}, // Auto-detects 'userId' column
121
+ organization: {},
122
+ owner: {},
132
123
  },
133
-
134
124
  crud: {
135
125
  list: { access: { roles: ["member", "admin"] } },
136
126
  get: { access: { roles: ["member", "admin"] } },
@@ -138,16 +128,13 @@ export default defineTable(todos, {
138
128
  update: { access: { roles: ["admin"] } },
139
129
  delete: { access: { roles: ["admin"] }, mode: "soft" },
140
130
  },
141
-
142
131
  guards: {
143
132
  createable: ["title", "description", "completed"],
144
133
  updatable: ["title", "description", "completed"],
145
134
  },
146
-
147
135
  masking: {
148
136
  userId: { type: "redact", show: { roles: ["admin"] } },
149
137
  },
150
-
151
138
  layouts: {
152
139
  default: {
153
140
  sections: [
@@ -159,12 +146,11 @@ export default defineTable(todos, {
159
146
  });
160
147
  ```
161
148
 
162
- ## Internal/Junction Tables (No API)
149
+ ### Internal/Junction Tables (No API)
163
150
 
164
151
  Tables without a `defineTable()` default export get no API routes:
165
152
 
166
153
  ```typescript
167
- // No default export = no API routes generated
168
154
  export const roomAmenities = sqliteTable('room_amenities', {
169
155
  roomId: text('room_id').notNull(),
170
156
  amenityId: text('amenity_id').notNull(),
@@ -173,9 +159,22 @@ export const roomAmenities = sqliteTable('room_amenities', {
173
159
 
174
160
  ---
175
161
 
176
- # Security Pillars
162
+ ## Four Security Layers
177
163
 
178
- ## 1. Firewall Data Isolation
164
+ Every API request passes through four layers in order:
165
+
166
+ ```
167
+ Request → Firewall → Access → Guards → Database → Masking → Response
168
+ ```
169
+
170
+ | Layer | Purpose | Failure |
171
+ |-------|---------|---------|
172
+ | **Firewall** | Tenant isolation via automatic WHERE clauses | 404 |
173
+ | **Access** | Role-based CRUD permissions (deny by default) | 403 |
174
+ | **Guards** | Field modification rules (protected/immutable fields) | 400 |
175
+ | **Masking** | PII redaction for unauthorized viewers | — |
176
+
177
+ ### 1. Firewall — Data Isolation
179
178
 
180
179
  Compiles WHERE clauses to isolate data by ownership. Auto-detects column names.
181
180
 
@@ -189,32 +188,25 @@ firewall: {
189
188
  }
190
189
  ```
191
190
 
192
- ### Auto-Detection
193
-
194
- Quickback finds columns by convention:
191
+ Auto-detection:
195
192
  - `organization` → looks for `organizationId` or `organization_id`
196
193
  - `owner` → looks for `userId` or `user_id`
197
194
  - `team` → looks for `teamId` or `team_id`
198
195
 
199
- ### Column Overrides
196
+ Column overrides:
200
197
 
201
198
  ```typescript
202
199
  firewall: {
203
200
  organization: {
204
- column: 'tenant_id', // Instead of 'organizationId'
205
- source: 'ctx.tenant.id', // Instead of 'ctx.activeOrgId'
201
+ column: 'tenant_id',
202
+ source: 'ctx.tenant.id',
206
203
  },
207
204
  }
208
205
  ```
209
206
 
210
- ### Owner Modes
207
+ Owner modes: default = strict (only own records), `optional` = own + NULL owner (admin pattern).
211
208
 
212
- | Mode | Behavior |
213
- |------|----------|
214
- | (default) | Strict — only own records |
215
- | `optional` | Own records + records with NULL owner (admin pattern) |
216
-
217
- ## 2. Access — Permission Checks
209
+ ### 2. Access Permission Checks
218
210
 
219
211
  Role-based and record-based access control. Deny by default.
220
212
 
@@ -235,30 +227,11 @@ crud: {
235
227
  }
236
228
  ```
237
229
 
238
- ### Record Conditions
239
-
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 } }` |
248
-
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 |
230
+ Record condition operators: `equals`, `notEquals`, `in`, `notIn`, `greaterThan`, `lessThan`
258
231
 
259
- ## 3. Guards Field Protection
232
+ Context variables: `$ctx.userId`, `$ctx.activeOrgId`, `$ctx.activeTeamId`, `$ctx.roles`, `$ctx.isAnonymous`
260
233
 
261
- Controls which fields can be modified.
234
+ ### 3. Guards Field Protection
262
235
 
263
236
  ```typescript
264
237
  guards: {
@@ -269,168 +242,84 @@ guards: {
269
242
  }
270
243
  ```
271
244
 
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
281
-
282
- ### PUT / Upsert
283
-
284
- PUT is only available when BOTH conditions are met:
285
- - `generateId: false` in database config
286
- - `guards: false` (disables field protection)
245
+ System-managed fields (always protected): `createdAt`, `createdBy`, `modifiedAt`, `modifiedBy`, `deletedAt`, `deletedBy`
287
246
 
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
- ```
247
+ PUT/Upsert requires both `generateId: false` and `guards: false`.
298
248
 
299
- ## 4. Masking — PII Redaction
249
+ ### 4. Masking — PII Redaction
300
250
 
301
251
  ```typescript
302
252
  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
- },
253
+ ssn: { type: 'ssn', show: { roles: ['admin', 'hr'] } },
254
+ email: { type: 'email', show: { roles: ['admin'], or: 'owner' } },
311
255
  }
312
256
  ```
313
257
 
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 |
258
+ Mask types: `email`, `phone`, `ssn`, `creditCard`, `name`, `redact`, `custom`
325
259
 
326
- ### Auto-Detection
327
-
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` |
260
+ Auto-detection warns on unmasked sensitive columns (`email`, `phone`, `ssn`, `password`, etc.).
337
261
 
338
262
  ---
339
263
 
340
- # Views — Column-Level Security
264
+ ## Views — Column-Level Security
341
265
 
342
- Views are named projections that control which fields are visible based on role.
266
+ Named projections that control which fields are visible based on role.
343
267
 
344
268
  ```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
- },
269
+ views: {
270
+ summary: {
271
+ fields: ['id', 'name', 'email'],
272
+ access: { roles: ['member', 'admin'] },
357
273
  },
358
- });
274
+ full: {
275
+ fields: ['id', 'name', 'email', 'phone', 'ssn', 'address'],
276
+ access: { roles: ['admin'] },
277
+ },
278
+ }
359
279
  ```
360
280
 
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.
281
+ Generated endpoints: `GET /api/v1/{resource}/views/{viewName}`
366
282
 
367
283
  ---
368
284
 
369
- # Validation
370
-
371
- Field-level validation rules compiled into the API:
285
+ ## Validation
372
286
 
373
287
  ```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
- });
288
+ validation: {
289
+ name: { minLength: 1, maxLength: 100 },
290
+ capacity: { min: 1, max: 1000 },
291
+ roomType: { enum: ['meeting', 'conference', 'breakout'] },
292
+ email: { email: true },
293
+ code: { pattern: '^[A-Z]{3}$' },
294
+ }
386
295
  ```
387
296
 
388
297
  ---
389
298
 
390
- # CMS Record Layouts
299
+ ## CMS Record Layouts
391
300
 
392
- Control how fields are grouped on the CMS record detail page. Without layouts, fields are auto-grouped by naming heuristics.
301
+ Control how fields are grouped on the CMS record detail page.
393
302
 
394
303
  ```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
- },
304
+ layouts: {
305
+ default: {
306
+ sections: [
307
+ { label: "Contact Info", columns: 2, fields: ["name", "email", "phone"] },
308
+ { label: "Notes", collapsed: true, fields: ["notes", "internalNotes"] },
309
+ ],
411
310
  },
412
- });
311
+ }
413
312
  ```
414
313
 
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.
314
+ Section options: `label` (string), `fields` (string[]), `columns` (1|2, default 1), `collapsed` (boolean, default false). Unassigned fields go to "Other Fields".
425
315
 
426
316
  ---
427
317
 
428
- # Actions — Custom Business Logic
318
+ ## Actions — Custom Business Logic
429
319
 
430
- Actions are custom API endpoints for business logic beyond CRUD. Defined in a separate `actions.ts` file using `defineActions()`.
320
+ Defined in a separate `actions.ts` file using `defineActions()`.
431
321
 
432
322
  ```typescript
433
- // quickback/features/todos/actions.ts
434
323
  import { todos } from './todos';
435
324
  import { defineActions } from '@quickback/compiler';
436
325
  import { z } from 'zod';
@@ -438,73 +327,33 @@ import { z } from 'zod';
438
327
  export default defineActions(todos, {
439
328
  complete: {
440
329
  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
- },
330
+ input: z.object({ completedAt: z.string().datetime().optional() }),
331
+ guard: { roles: ["member", "admin"], record: { completed: { equals: false } } },
448
332
  execute: async ({ db, record, ctx, input }) => {
449
- // Inline handler
450
333
  await db.update(todos).set({ completed: true }).where(eq(todos.id, record.id));
451
334
  return { success: true };
452
335
  },
453
336
  sideEffects: "sync",
454
337
  },
455
-
456
338
  archive: {
457
339
  description: "Archive a todo",
458
340
  input: z.object({}),
459
341
  guard: { roles: ["admin"] },
460
- handler: "./handlers/archive", // OR: external file handler
342
+ handler: "./handlers/archive",
461
343
  },
462
344
  });
463
345
  ```
464
346
 
465
- **Generated Route**: `POST /api/v1/todos/:id/complete`
466
-
467
- ### Record-based vs Standalone Actions
347
+ **Record-based** (default): `POST /api/v1/{resource}/:id/{action}`
348
+ **Standalone**: `standalone: true`, custom `path` and `method`
468
349
 
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
495
-
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 |
350
+ Action options: `standalone`, `method` (GET/POST/PUT/PATCH/DELETE), `responseType` (json/stream/file), `sideEffects` (sync/async/fire-and-forget)
502
351
 
503
352
  ---
504
353
 
505
- # API Reference
354
+ ## API Reference
506
355
 
507
- ## CRUD Operations
356
+ ### CRUD Operations
508
357
 
509
358
  | Method | Endpoint | Description |
510
359
  |--------|----------|-------------|
@@ -515,278 +364,247 @@ chat: {
515
364
  | `DELETE` | `/api/v1/{resource}/:id` | Delete record |
516
365
  | `PUT` | `/api/v1/{resource}/:id` | Upsert (if enabled) |
517
366
 
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
367
+ ### Batch Operations
536
368
 
537
- ### Pagination
369
+ `POST|PATCH|DELETE|PUT /api/v1/{resource}/batch` with `{ "records": [...], "options": { "atomic": false } }`
538
370
 
539
- | Param | Default | Max |
540
- |-------|---------|-----|
541
- | `limit` | 50 | 100 |
542
- | `offset` | 0 | — |
371
+ ### Query Parameters
543
372
 
544
- ### Filtering Operators
373
+ | Feature | Syntax |
374
+ |---------|--------|
375
+ | Pagination | `?limit=50&offset=0` (max 100) |
376
+ | Filter | `?status=active`, `?amount.gt=100`, `?status.in=draft,pending` |
377
+ | Sort | `?sort=status,-createdAt` (multi-sort, `-` = desc) |
378
+ | Fields | `?fields=id,name,status` |
379
+ | Total count | `?total=true` |
380
+ | Search | `?search=conference` (OR'd LIKE across text columns) |
545
381
 
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')` |
382
+ Filter operators: `.gt`, `.gte`, `.lt`, `.lte`, `.ne`, `.like`, `.in`
556
383
 
557
- ### Sorting (Multi-Sort)
384
+ ### Response Format
558
385
 
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)
386
+ ```json
387
+ {
388
+ "data": [{ "id": "...", "title": "Todo 1" }],
389
+ "pagination": { "limit": 10, "offset": 0, "count": 2, "total": 42 }
390
+ }
563
391
  ```
564
392
 
565
- No prefix = ascending, `-` prefix = descending. When commas or `-` detected, `order` param is ignored.
393
+ ### Error Responses
566
394
 
567
- ### Field Selection
395
+ | Status | Layer | Meaning |
396
+ |--------|-------|---------|
397
+ | `401` | Auth | Invalid/expired session |
398
+ | `403` | Access | Insufficient permissions |
399
+ | `404` | Firewall | Record not found or outside scope |
400
+ | `400` | Guards | Invalid field modification |
568
401
 
569
- ```
570
- ?fields=id,name,status # Return only these columns (LIST + GET routes)
571
- ```
402
+ ---
572
403
 
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.
404
+ ## Database Dialects
574
405
 
575
- ### Total Count
406
+ | Stack | Import | Table Function |
407
+ |-------|--------|----------------|
408
+ | Cloudflare D1 / SQLite | `drizzle-orm/sqlite-core` | `sqliteTable` |
409
+ | Supabase / PostgreSQL | `drizzle-orm/pg-core` | `pgTable` |
410
+ | MySQL | `drizzle-orm/mysql-core` | `mysqlTable` |
576
411
 
577
- ```
578
- ?total=true # Include pagination.total (LIST + VIEW routes)
579
- ```
412
+ ---
580
413
 
581
- Opt-in extra `COUNT(*)` query. Returns total matching records across all pages.
414
+ # PART 2: THE QUICKBACK STACK
582
415
 
583
- ### Search
416
+ 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
417
 
585
418
  ```
586
- ?search=conference # Full-text search across text columns (LIST + VIEW routes)
419
+ ┌─────────────────────────────────────────────────────────┐
420
+ │ QUICKBACK STACK ON CLOUDFLARE │
421
+ │ │
422
+ │ Workers — API runtime (Hono) │
423
+ │ D1 — SQLite database at the edge │
424
+ │ R2 — S3-compatible file storage │
425
+ │ KV — Distributed key-value store │
426
+ │ Durable Objects — Realtime WebSockets │
427
+ │ Queues — Background job processing │
428
+ │ Workers AI — Embeddings & vector search │
429
+ │ Better Auth — Authentication & organizations │
430
+ └─────────────────────────────────────────────────────────┘
587
431
  ```
588
432
 
589
- OR'd LIKE across all `text()` schema columns (system columns excluded). Combine with filters: `?search=urgent&status=active`.
433
+ ## Authentication (Better Auth)
590
434
 
591
- ## Response Format
435
+ Built on Better Auth with multi-tenant organization support.
592
436
 
593
- ```json
594
- {
595
- "data": [{ "id": "...", "title": "Todo 1" }],
596
- "pagination": {
597
- "limit": 10,
598
- "offset": 0,
599
- "count": 2,
600
- "total": 42
601
- }
437
+ - Email/password, magic links, passkeys, email OTP
438
+ - Three org roles: `owner`, `admin`, `member`
439
+ - Session storage in KV namespace
440
+ - All auth routes at `/auth/v1/*`
441
+ - Extensible via Better Auth plugins
442
+
443
+ ```typescript
444
+ // Config
445
+ providers: {
446
+ auth: defineAuth("better-auth"),
602
447
  }
603
448
  ```
604
449
 
605
- `count` = records on this page. `total` = total matching records (only when `?total=true`).
450
+ Docs: `quickback docs stack/auth/using-auth` | https://docs.quickback.dev/stack/auth
606
451
 
607
- ---
452
+ ## Database (D1)
453
+
454
+ SQLite at the edge with zero configuration. Multi-database pattern for independent scaling:
608
455
 
609
- # Webhooks
456
+ | Binding | Purpose |
457
+ |---------|---------|
458
+ | `DB` | Application data (features) |
459
+ | `AUTH_DB` | Auth sessions & users |
460
+ | `FILES_DB` | File metadata |
461
+ | `WEBHOOKS_DB` | Webhook tracking |
610
462
 
611
- ## Configuration
463
+ - Drizzle ORM for type-safe queries
464
+ - Auto-generated migrations on compile
465
+ - Application-layer security (firewall, access, guards, masking)
466
+ - Local dev with `.wrangler/state/`
467
+ - Neon Postgres available as alternative
612
468
 
613
469
  ```typescript
614
- // quickback.config.ts
615
470
  providers: {
616
471
  database: defineDatabase("cloudflare-d1", {
617
472
  splitDatabases: true,
618
- webhooksBinding: "WEBHOOKS_DB",
619
473
  }),
620
474
  }
621
475
  ```
622
476
 
623
- ## Inbound Webhooks (Receiving)
477
+ Docs: `quickback docs stack/database/d1` | https://docs.quickback.dev/stack/database/d1
624
478
 
625
- ```typescript
626
- import { onWebhookEvent } from './lib/webhooks';
479
+ ## File Storage (R2)
627
480
 
628
- onWebhookEvent('stripe:checkout.session.completed', async (ctx) => {
629
- const { data, env } = ctx;
630
- await createSubscription({ ... });
631
- });
632
- ```
481
+ S3-compatible object storage with built-in access control.
633
482
 
634
- ## Outbound Webhooks (Sending)
483
+ - Two-worker architecture: API worker (upload/manage) + Files worker (serve)
484
+ - Bucket-scoped access policies (public, organization, user)
485
+ - File metadata tracked in `FILES_DB`
486
+ - Soft deletes with org-level tenant isolation
487
+ - API at `/storage/v1/*`
635
488
 
636
- ```typescript
637
- import { emitWebhookEvent } from './lib/webhooks';
489
+ Docs: `quickback docs stack/storage/r2` | https://docs.quickback.dev/stack/storage/r2
638
490
 
639
- await emitWebhookEvent(
640
- 'user.created',
641
- { id: user.id, email: user.email },
642
- { organizationId: orgId },
643
- env
644
- );
645
- ```
491
+ ## KV Storage
646
492
 
647
- ## Available Events
493
+ Distributed key-value store optimized for reads (300+ edge locations, sub-millisecond).
648
494
 
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 |
495
+ - Use cases: sessions, caching, rate limiting, feature flags
496
+ - Eventually consistent (60 second propagation)
497
+ - TTL/expiration support
656
498
 
657
- **Wildcards**: `user.*`, `subscription.*`, `organization.*`, `file.*`, `*`
499
+ Docs: `quickback docs stack/storage/kv` | https://docs.quickback.dev/stack/storage/kv
658
500
 
659
- ---
501
+ ## Realtime (Durable Objects)
660
502
 
661
- # CLI Commands
503
+ WebSocket connections for live updates via organization-scoped Durable Objects.
662
504
 
663
- ## Core Commands
505
+ - CRUD event broadcasting (insert, update, delete)
506
+ - Custom broadcasts and event namespaces
507
+ - Role-based filtering and per-role field masking
508
+ - Session token or API key authentication
509
+ - Type-safe with `defineRealtime()` for custom events
510
+ - Endpoint: `/realtime/v1/websocket`
664
511
 
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 |
512
+ Docs: `quickback docs stack/realtime/durable-objects` | https://docs.quickback.dev/stack/realtime
672
513
 
673
- ## Available Templates
514
+ ## Queues (Background Processing)
674
515
 
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 |
516
+ Cloudflare Queues for reliable async job processing.
680
517
 
681
- ## Claude Code Integration
518
+ | Queue | Purpose |
519
+ |-------|---------|
520
+ | `EMBEDDINGS_QUEUE` | Auto-generate embeddings |
521
+ | `WEBHOOKS_QUEUE` | Webhook delivery |
522
+ | Custom queues | Your background jobs |
682
523
 
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 |
524
+ - Custom handlers via `defineQueue()` in `services/queues/`
525
+ - Auto-retry up to 3 times, max batch 10, 30s timeout
526
+ - Chaining between queues
691
527
 
692
- ---
528
+ Docs: `quickback docs stack/queues/using-queues` | https://docs.quickback.dev/stack/queues
693
529
 
694
- # Development Workflow
530
+ ## Vector & Embeddings (Workers AI)
695
531
 
696
- ```bash
697
- # 1. Create project
698
- quickback create cloudflare my-app
532
+ Auto-generated embeddings for similarity search and classification.
699
533
 
700
- # 2. Define features in quickback/features/
534
+ - Table-level config via `embeddings` in `defineTable()`
535
+ - Service-level config via `defineEmbedding()`
536
+ - Default model: `@cf/baai/bge-base-en-v1.5` (768 dimensions)
537
+ - Async processing via `EMBEDDINGS_QUEUE`
538
+ - Vector storage in D1 + optional Vectorize index
539
+ - API: `/api/v1/embeddings`
701
540
 
702
- # 3. Compile
703
- quickback compile
541
+ Docs: `quickback docs stack/vector/embeddings` | https://docs.quickback.dev/stack/vector
704
542
 
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
543
+ ## Webhooks
715
544
 
716
- ## Cloudflare Stack (Full Edge)
545
+ ### Inbound (Receiving)
717
546
 
718
- Hono API on Workers, D1 database, Better Auth, R2 file storage, KV, Queues, Durable Objects for realtime.
547
+ Receive events from Stripe, Paddle, GitHub, etc. at `POST /webhooks/v1/inbound/:provider`
719
548
 
720
549
  ```typescript
721
- defineConfig({
722
- providers: {
723
- runtime: defineRuntime("cloudflare"),
724
- database: defineDatabase("cloudflare-d1"),
725
- auth: defineAuth("better-auth"),
726
- },
550
+ import { onWebhookEvent } from './lib/webhooks';
551
+
552
+ onWebhookEvent('stripe:checkout.session.completed', async (ctx) => {
553
+ const { data, env } = ctx;
554
+ await createSubscription({ ... });
727
555
  });
728
556
  ```
729
557
 
730
- ## Supabase (RLS Policies)
558
+ ### Outbound (Sending)
731
559
 
732
- Compiles Quickback definitions into Row Level Security policies for Postgres-level security.
560
+ Emit signed events when data changes. HMAC-SHA256 signing, async delivery via queue with exponential backoff retry.
733
561
 
734
562
  ```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.
563
+ import { emitWebhookEvent } from './lib/webhooks';
747
564
 
748
- ---
565
+ await emitWebhookEvent('user.created', { id: user.id, email: user.email }, { organizationId: orgId }, env);
566
+ ```
749
567
 
750
- # Error Responses
568
+ Event patterns: `user.*`, `subscription.*`, `organization.*`, `file.*`, `*`
569
+ Endpoint management: `/webhooks/v1/endpoints`
751
570
 
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 |
571
+ Docs: `quickback docs stack/webhooks/outbound` | https://docs.quickback.dev/stack/webhooks
759
572
 
760
573
  ---
761
574
 
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` |
575
+ # CLI Commands
771
576
 
772
- ---
577
+ | Command | Description |
578
+ |---------|-------------|
579
+ | `quickback create <template> <name>` | Create project from template |
580
+ | `quickback compile` | Compile definitions to output |
581
+ | `quickback docs [topic]` | Browse built-in documentation |
582
+ | `quickback login` | Authenticate for Pro templates |
583
+ | `quickback logout` | Clear stored credentials |
584
+ | `quickback whoami` | Show current auth status |
585
+ | `quickback claude install` | Install Claude Code skill |
586
+ | `quickback claude update` | Update to latest skill version |
773
587
 
774
- # Documentation
588
+ ## Available Templates
775
589
 
776
- ## CLI Docs (Recommended)
590
+ | Template | Stack | Status |
591
+ |----------|-------|--------|
592
+ | `cloudflare` | Cloudflare Workers + D1 + Better Auth | Free |
593
+ | `bun` | Bun + SQLite + Better Auth | Free |
594
+ | `turso` | Turso/LibSQL + Better Auth | Pro |
777
595
 
778
- The fastest way to access documentation is via the Quickback CLI. It bundles all docs offline:
596
+ ## Development Workflow
779
597
 
780
598
  ```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
599
+ quickback create cloudflare my-app # 1. Create project
600
+ # Define features in quickback/features/
601
+ quickback compile # 2. Compile
602
+ cd dist && npm install && npm run dev # 3. Run
785
603
  ```
786
604
 
787
- ## Online Docs
605
+ ---
788
606
 
789
- Full documentation at https://docs.quickback.dev
607
+ # Online Documentation
790
608
 
791
609
  - [Getting Started](https://docs.quickback.dev/compiler/getting-started)
792
610
  - [Concepts](https://docs.quickback.dev/compiler/definitions/concepts)
@@ -796,11 +614,16 @@ Full documentation at https://docs.quickback.dev
796
614
  - [Guards](https://docs.quickback.dev/compiler/definitions/guards)
797
615
  - [Masking](https://docs.quickback.dev/compiler/definitions/masking)
798
616
  - [Views](https://docs.quickback.dev/compiler/definitions/views)
799
- - [Validation](https://docs.quickback.dev/compiler/definitions/validation)
800
617
  - [Actions](https://docs.quickback.dev/compiler/definitions/actions)
801
618
  - [CRUD API](https://docs.quickback.dev/compiler/using-the-api/crud)
802
619
  - [Query Parameters](https://docs.quickback.dev/compiler/using-the-api/query-params)
803
620
  - [CLI Reference](https://docs.quickback.dev/compiler/cloud-compiler/cli)
621
+ - [Stack Overview](https://docs.quickback.dev/stack)
622
+ - [D1 Database](https://docs.quickback.dev/stack/database/d1)
623
+ - [R2 Storage](https://docs.quickback.dev/stack/storage/r2)
624
+ - [Realtime](https://docs.quickback.dev/stack/realtime)
625
+ - [Queues](https://docs.quickback.dev/stack/queues)
626
+ - [Embeddings](https://docs.quickback.dev/stack/vector)
627
+ - [Webhooks](https://docs.quickback.dev/stack/webhooks)
804
628
  - [CMS Overview](https://docs.quickback.dev/cms)
805
629
  - [CMS Record Layouts](https://docs.quickback.dev/cms/record-layouts)
806
- - [CMS Schema Format](https://docs.quickback.dev/cms/schema-format)