@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 +1 -1
- package/package.json +1 -1
- package/src/skill/SKILL.md +267 -442
package/dist/index.js
CHANGED
package/package.json
CHANGED
package/src/skill/SKILL.md
CHANGED
|
@@ -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
|
|
7
|
+
# Quickback
|
|
8
8
|
|
|
9
|
-
Quickback
|
|
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
|
-
##
|
|
16
|
+
## Accessing Documentation
|
|
17
|
+
|
|
18
|
+
The fastest way to look up detailed docs is via the CLI:
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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: {},
|
|
131
|
-
owner: {},
|
|
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
|
-
|
|
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
|
-
|
|
164
|
+
## Four Security Layers
|
|
177
165
|
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
+
Column overrides:
|
|
200
199
|
|
|
201
200
|
```typescript
|
|
202
201
|
firewall: {
|
|
203
202
|
organization: {
|
|
204
|
-
column: 'tenant_id',
|
|
205
|
-
source: 'ctx.tenant.id',
|
|
203
|
+
column: 'tenant_id',
|
|
204
|
+
source: 'ctx.tenant.id',
|
|
206
205
|
},
|
|
207
206
|
}
|
|
208
207
|
```
|
|
209
208
|
|
|
210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
232
|
+
Record condition operators: `equals`, `notEquals`, `in`, `notIn`, `greaterThan`, `lessThan`
|
|
239
233
|
|
|
240
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
249
|
+
PUT/Upsert requires both `generateId: false` and `guards: false`.
|
|
283
250
|
|
|
284
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
266
|
+
## Views — Column-Level Security
|
|
341
267
|
|
|
342
|
-
|
|
268
|
+
Named projections that control which fields are visible based on role.
|
|
343
269
|
|
|
344
270
|
```typescript
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
Field-level validation rules compiled into the API:
|
|
287
|
+
## Validation
|
|
372
288
|
|
|
373
289
|
```typescript
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
301
|
+
## CMS Record Layouts
|
|
391
302
|
|
|
392
|
-
Control how fields are grouped on the CMS record detail page.
|
|
303
|
+
Control how fields are grouped on the CMS record detail page.
|
|
393
304
|
|
|
394
305
|
```typescript
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
-
|
|
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
|
-
|
|
320
|
+
## Actions — Custom Business Logic
|
|
429
321
|
|
|
430
|
-
|
|
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
|
-
|
|
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",
|
|
344
|
+
handler: "./handlers/archive",
|
|
461
345
|
},
|
|
462
346
|
});
|
|
463
347
|
```
|
|
464
348
|
|
|
465
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
356
|
+
## API Reference
|
|
506
357
|
|
|
507
|
-
|
|
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
|
-
|
|
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
|
-
|
|
371
|
+
`POST|PATCH|DELETE|PUT /api/v1/{resource}/batch` with `{ "records": [...], "options": { "atomic": false } }`
|
|
538
372
|
|
|
539
|
-
|
|
540
|
-
|-------|---------|-----|
|
|
541
|
-
| `limit` | 50 | 100 |
|
|
542
|
-
| `offset` | 0 | — |
|
|
373
|
+
### Query Parameters
|
|
543
374
|
|
|
544
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
386
|
+
### Response Format
|
|
558
387
|
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
-
|
|
395
|
+
### Error Responses
|
|
566
396
|
|
|
567
|
-
|
|
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
|
-
|
|
406
|
+
## Database Dialects
|
|
574
407
|
|
|
575
|
-
|
|
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
|
-
|
|
416
|
+
# PART 2: THE QUICKBACK STACK
|
|
582
417
|
|
|
583
|
-
|
|
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
|
-
|
|
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
|
-
|
|
435
|
+
## Authentication (Better Auth)
|
|
590
436
|
|
|
591
|
-
|
|
437
|
+
Built on Better Auth with multi-tenant organization support.
|
|
592
438
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
-
`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
479
|
+
Docs: `quickback docs stack/database/d1` | https://docs.quickback.dev/stack/database/d1
|
|
624
480
|
|
|
625
|
-
|
|
626
|
-
import { onWebhookEvent } from './lib/webhooks';
|
|
481
|
+
## File Storage (R2)
|
|
627
482
|
|
|
628
|
-
|
|
629
|
-
const { data, env } = ctx;
|
|
630
|
-
await createSubscription({ ... });
|
|
631
|
-
});
|
|
632
|
-
```
|
|
483
|
+
S3-compatible object storage with built-in access control.
|
|
633
484
|
|
|
634
|
-
|
|
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
|
-
|
|
637
|
-
import { emitWebhookEvent } from './lib/webhooks';
|
|
491
|
+
Docs: `quickback docs stack/storage/r2` | https://docs.quickback.dev/stack/storage/r2
|
|
638
492
|
|
|
639
|
-
|
|
640
|
-
'user.created',
|
|
641
|
-
{ id: user.id, email: user.email },
|
|
642
|
-
{ organizationId: orgId },
|
|
643
|
-
env
|
|
644
|
-
);
|
|
645
|
-
```
|
|
493
|
+
## KV Storage
|
|
646
494
|
|
|
647
|
-
|
|
495
|
+
Distributed key-value store optimized for reads (300+ edge locations, sub-millisecond).
|
|
648
496
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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
|
-
|
|
501
|
+
Docs: `quickback docs stack/storage/kv` | https://docs.quickback.dev/stack/storage/kv
|
|
658
502
|
|
|
659
|
-
|
|
503
|
+
## Realtime (Durable Objects)
|
|
660
504
|
|
|
661
|
-
|
|
505
|
+
WebSocket connections for live updates via organization-scoped Durable Objects.
|
|
662
506
|
|
|
663
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
516
|
+
## Queues (Background Processing)
|
|
674
517
|
|
|
675
|
-
|
|
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
|
-
|
|
520
|
+
| Queue | Purpose |
|
|
521
|
+
|-------|---------|
|
|
522
|
+
| `EMBEDDINGS_QUEUE` | Auto-generate embeddings |
|
|
523
|
+
| `WEBHOOKS_QUEUE` | Webhook delivery |
|
|
524
|
+
| Custom queues | Your background jobs |
|
|
682
525
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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
|
-
|
|
532
|
+
## Vector & Embeddings (Workers AI)
|
|
695
533
|
|
|
696
|
-
|
|
697
|
-
# 1. Create project
|
|
698
|
-
quickback create cloudflare my-app
|
|
534
|
+
Auto-generated embeddings for similarity search and classification.
|
|
699
535
|
|
|
700
|
-
|
|
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
|
-
|
|
703
|
-
quickback compile
|
|
543
|
+
Docs: `quickback docs stack/vector/embeddings` | https://docs.quickback.dev/stack/vector
|
|
704
544
|
|
|
705
|
-
|
|
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
|
-
|
|
547
|
+
### Inbound (Receiving)
|
|
717
548
|
|
|
718
|
-
|
|
549
|
+
Receive events from Stripe, Paddle, GitHub, etc. at `POST /webhooks/v1/inbound/:provider`
|
|
719
550
|
|
|
720
551
|
```typescript
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
|
|
560
|
+
### Outbound (Sending)
|
|
731
561
|
|
|
732
|
-
|
|
562
|
+
Emit signed events when data changes. HMAC-SHA256 signing, async delivery via queue with exponential backoff retry.
|
|
733
563
|
|
|
734
564
|
```typescript
|
|
735
|
-
|
|
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
|
-
|
|
570
|
+
Event patterns: `user.*`, `subscription.*`, `organization.*`, `file.*`, `*`
|
|
571
|
+
Endpoint management: `/webhooks/v1/endpoints`
|
|
751
572
|
|
|
752
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
590
|
+
## Available Templates
|
|
775
591
|
|
|
776
|
-
|
|
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
|
-
|
|
598
|
+
## Development Workflow
|
|
779
599
|
|
|
780
600
|
```bash
|
|
781
|
-
quickback
|
|
782
|
-
|
|
783
|
-
quickback
|
|
784
|
-
|
|
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
|
-
|
|
607
|
+
---
|
|
788
608
|
|
|
789
|
-
|
|
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)
|