@kardoe/quickback 0.5.13 → 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 +297 -409
- package/src/skill/agents/quickback-specialist/AGENT.md +15 -0
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:
|
|
19
|
+
|
|
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
|
+
```
|
|
26
|
+
|
|
27
|
+
Full online docs: https://docs.quickback.dev
|
|
28
|
+
|
|
29
|
+
---
|
|
14
30
|
|
|
15
|
-
|
|
16
|
-
2. **Compile** — Quickback generates API routes, middleware, migrations, and RLS policies
|
|
17
|
-
3. **Deploy** — Standard tooling to Cloudflare Workers, Supabase, or Neon
|
|
31
|
+
# PART 1: THE COMPILER
|
|
18
32
|
|
|
19
|
-
|
|
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,24 +130,29 @@ 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
|
},
|
|
140
|
+
layouts: {
|
|
141
|
+
default: {
|
|
142
|
+
sections: [
|
|
143
|
+
{ label: "Details", columns: 2, fields: ["title", "completed"] },
|
|
144
|
+
{ label: "Audit", collapsed: true, fields: ["userId"] },
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
150
148
|
});
|
|
151
149
|
```
|
|
152
150
|
|
|
153
|
-
|
|
151
|
+
### Internal/Junction Tables (No API)
|
|
154
152
|
|
|
155
153
|
Tables without a `defineTable()` default export get no API routes:
|
|
156
154
|
|
|
157
155
|
```typescript
|
|
158
|
-
// No default export = no API routes generated
|
|
159
156
|
export const roomAmenities = sqliteTable('room_amenities', {
|
|
160
157
|
roomId: text('room_id').notNull(),
|
|
161
158
|
amenityId: text('amenity_id').notNull(),
|
|
@@ -164,9 +161,22 @@ export const roomAmenities = sqliteTable('room_amenities', {
|
|
|
164
161
|
|
|
165
162
|
---
|
|
166
163
|
|
|
167
|
-
|
|
164
|
+
## Four Security Layers
|
|
165
|
+
|
|
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 | — |
|
|
168
178
|
|
|
169
|
-
|
|
179
|
+
### 1. Firewall — Data Isolation
|
|
170
180
|
|
|
171
181
|
Compiles WHERE clauses to isolate data by ownership. Auto-detects column names.
|
|
172
182
|
|
|
@@ -180,32 +190,25 @@ firewall: {
|
|
|
180
190
|
}
|
|
181
191
|
```
|
|
182
192
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
Quickback finds columns by convention:
|
|
193
|
+
Auto-detection:
|
|
186
194
|
- `organization` → looks for `organizationId` or `organization_id`
|
|
187
195
|
- `owner` → looks for `userId` or `user_id`
|
|
188
196
|
- `team` → looks for `teamId` or `team_id`
|
|
189
197
|
|
|
190
|
-
|
|
198
|
+
Column overrides:
|
|
191
199
|
|
|
192
200
|
```typescript
|
|
193
201
|
firewall: {
|
|
194
202
|
organization: {
|
|
195
|
-
column: 'tenant_id',
|
|
196
|
-
source: 'ctx.tenant.id',
|
|
203
|
+
column: 'tenant_id',
|
|
204
|
+
source: 'ctx.tenant.id',
|
|
197
205
|
},
|
|
198
206
|
}
|
|
199
207
|
```
|
|
200
208
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
| Mode | Behavior |
|
|
204
|
-
|------|----------|
|
|
205
|
-
| (default) | Strict — only own records |
|
|
206
|
-
| `optional` | Own records + records with NULL owner (admin pattern) |
|
|
209
|
+
Owner modes: default = strict (only own records), `optional` = own + NULL owner (admin pattern).
|
|
207
210
|
|
|
208
|
-
|
|
211
|
+
### 2. Access — Permission Checks
|
|
209
212
|
|
|
210
213
|
Role-based and record-based access control. Deny by default.
|
|
211
214
|
|
|
@@ -226,30 +229,11 @@ crud: {
|
|
|
226
229
|
}
|
|
227
230
|
```
|
|
228
231
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
| Operator | Example |
|
|
232
|
-
|----------|---------|
|
|
233
|
-
| `equals` | `{ status: { equals: 'active' } }` |
|
|
234
|
-
| `notEquals` | `{ status: { notEquals: 'archived' } }` |
|
|
235
|
-
| `in` | `{ status: { in: ['active', 'pending'] } }` |
|
|
236
|
-
| `notIn` | `{ status: { notIn: ['deleted'] } }` |
|
|
237
|
-
| `greaterThan` | `{ amount: { greaterThan: 0 } }` |
|
|
238
|
-
| `lessThan` | `{ amount: { lessThan: 10000 } }` |
|
|
232
|
+
Record condition operators: `equals`, `notEquals`, `in`, `notIn`, `greaterThan`, `lessThan`
|
|
239
233
|
|
|
240
|
-
|
|
234
|
+
Context variables: `$ctx.userId`, `$ctx.activeOrgId`, `$ctx.activeTeamId`, `$ctx.roles`, `$ctx.isAnonymous`
|
|
241
235
|
|
|
242
|
-
|
|
243
|
-
|----------|------|-------------|
|
|
244
|
-
| `$ctx.userId` | string | Current user's ID |
|
|
245
|
-
| `$ctx.activeOrgId` | string | Active organization ID |
|
|
246
|
-
| `$ctx.activeTeamId` | string \| null | Active team ID |
|
|
247
|
-
| `$ctx.roles` | string[] | User's roles in active org |
|
|
248
|
-
| `$ctx.isAnonymous` | boolean | Anonymous user flag |
|
|
249
|
-
|
|
250
|
-
## 3. Guards — Field Protection
|
|
251
|
-
|
|
252
|
-
Controls which fields can be modified.
|
|
236
|
+
### 3. Guards — Field Protection
|
|
253
237
|
|
|
254
238
|
```typescript
|
|
255
239
|
guards: {
|
|
@@ -260,130 +244,84 @@ guards: {
|
|
|
260
244
|
}
|
|
261
245
|
```
|
|
262
246
|
|
|
263
|
-
|
|
264
|
-
- Field can't be in both `createable` and `protected`
|
|
265
|
-
- Field can't be in both `updatable` and `immutable`
|
|
266
|
-
- All referenced fields must exist in the schema
|
|
267
|
-
|
|
268
|
-
**System-managed fields** (always protected):
|
|
269
|
-
- `createdAt`, `createdBy` — Set on INSERT
|
|
270
|
-
- `modifiedAt`, `modifiedBy` — Set on INSERT/UPDATE
|
|
271
|
-
- `deletedAt`, `deletedBy` — Set on soft DELETE
|
|
247
|
+
System-managed fields (always protected): `createdAt`, `createdBy`, `modifiedAt`, `modifiedBy`, `deletedAt`, `deletedBy`
|
|
272
248
|
|
|
273
|
-
|
|
249
|
+
PUT/Upsert requires both `generateId: false` and `guards: false`.
|
|
274
250
|
|
|
275
|
-
|
|
276
|
-
- `generateId: false` in database config
|
|
277
|
-
- `guards: false` (disables field protection)
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
export default defineTable(external_orders, {
|
|
281
|
-
guards: false,
|
|
282
|
-
crud: {
|
|
283
|
-
put: { access: { roles: ['sync-service'] } },
|
|
284
|
-
get: {},
|
|
285
|
-
list: {},
|
|
286
|
-
},
|
|
287
|
-
});
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
## 4. Masking — PII Redaction
|
|
251
|
+
### 4. Masking — PII Redaction
|
|
291
252
|
|
|
292
253
|
```typescript
|
|
293
254
|
masking: {
|
|
294
|
-
ssn: {
|
|
295
|
-
|
|
296
|
-
show: { roles: ['admin', 'hr'] },
|
|
297
|
-
},
|
|
298
|
-
email: {
|
|
299
|
-
type: 'email', // p***@e******.com
|
|
300
|
-
show: { roles: ['admin'], or: 'owner' },
|
|
301
|
-
},
|
|
255
|
+
ssn: { type: 'ssn', show: { roles: ['admin', 'hr'] } },
|
|
256
|
+
email: { type: 'email', show: { roles: ['admin'], or: 'owner' } },
|
|
302
257
|
}
|
|
303
258
|
```
|
|
304
259
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
| Type | Output |
|
|
308
|
-
|------|--------|
|
|
309
|
-
| `email` | `p***@e******.com` |
|
|
310
|
-
| `phone` | `******4567` |
|
|
311
|
-
| `ssn` | `*****6789` |
|
|
312
|
-
| `creditCard` | `************1234` |
|
|
313
|
-
| `name` | `J*** D**` |
|
|
314
|
-
| `redact` | `[REDACTED]` |
|
|
315
|
-
| `custom` | Your function |
|
|
260
|
+
Mask types: `email`, `phone`, `ssn`, `creditCard`, `name`, `redact`, `custom`
|
|
316
261
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
The compiler warns if sensitive columns aren't explicitly configured:
|
|
320
|
-
|
|
321
|
-
| Column Pattern | Default Mask |
|
|
322
|
-
|---------------|-------------|
|
|
323
|
-
| `email` | `email` |
|
|
324
|
-
| `phone`, `mobile`, `fax` | `phone` |
|
|
325
|
-
| `ssn`, `socialsecurity` | `ssn` |
|
|
326
|
-
| `creditcard`, `cc` | `creditCard` |
|
|
327
|
-
| `password`, `secret`, `token` | `redact` |
|
|
262
|
+
Auto-detection warns on unmasked sensitive columns (`email`, `phone`, `ssn`, `password`, etc.).
|
|
328
263
|
|
|
329
264
|
---
|
|
330
265
|
|
|
331
|
-
|
|
266
|
+
## Views — Column-Level Security
|
|
332
267
|
|
|
333
|
-
|
|
268
|
+
Named projections that control which fields are visible based on role.
|
|
334
269
|
|
|
335
270
|
```typescript
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
summary: {
|
|
341
|
-
fields: ['id', 'name', 'email'],
|
|
342
|
-
access: { roles: ['member', 'admin'] },
|
|
343
|
-
},
|
|
344
|
-
full: {
|
|
345
|
-
fields: ['id', 'name', 'email', 'phone', 'ssn', 'address'],
|
|
346
|
-
access: { roles: ['admin'] },
|
|
347
|
-
},
|
|
271
|
+
views: {
|
|
272
|
+
summary: {
|
|
273
|
+
fields: ['id', 'name', 'email'],
|
|
274
|
+
access: { roles: ['member', 'admin'] },
|
|
348
275
|
},
|
|
349
|
-
|
|
276
|
+
full: {
|
|
277
|
+
fields: ['id', 'name', 'email', 'phone', 'ssn', 'address'],
|
|
278
|
+
access: { roles: ['admin'] },
|
|
279
|
+
},
|
|
280
|
+
}
|
|
350
281
|
```
|
|
351
282
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
283
|
+
Generated endpoints: `GET /api/v1/{resource}/views/{viewName}`
|
|
284
|
+
|
|
285
|
+
---
|
|
355
286
|
|
|
356
|
-
|
|
287
|
+
## Validation
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
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
|
+
}
|
|
297
|
+
```
|
|
357
298
|
|
|
358
299
|
---
|
|
359
300
|
|
|
360
|
-
|
|
301
|
+
## CMS Record Layouts
|
|
361
302
|
|
|
362
|
-
|
|
303
|
+
Control how fields are grouped on the CMS record detail page.
|
|
363
304
|
|
|
364
305
|
```typescript
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
roomType: { enum: ['meeting', 'conference', 'breakout'] },
|
|
372
|
-
email: { email: true },
|
|
373
|
-
website: { url: true },
|
|
374
|
-
code: { pattern: '^[A-Z]{3}$' },
|
|
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
|
+
],
|
|
375
312
|
},
|
|
376
|
-
}
|
|
313
|
+
}
|
|
377
314
|
```
|
|
378
315
|
|
|
316
|
+
Section options: `label` (string), `fields` (string[]), `columns` (1|2, default 1), `collapsed` (boolean, default false). Unassigned fields go to "Other Fields".
|
|
317
|
+
|
|
379
318
|
---
|
|
380
319
|
|
|
381
|
-
|
|
320
|
+
## Actions — Custom Business Logic
|
|
382
321
|
|
|
383
|
-
|
|
322
|
+
Defined in a separate `actions.ts` file using `defineActions()`.
|
|
384
323
|
|
|
385
324
|
```typescript
|
|
386
|
-
// quickback/features/todos/actions.ts
|
|
387
325
|
import { todos } from './todos';
|
|
388
326
|
import { defineActions } from '@quickback/compiler';
|
|
389
327
|
import { z } from 'zod';
|
|
@@ -391,73 +329,33 @@ import { z } from 'zod';
|
|
|
391
329
|
export default defineActions(todos, {
|
|
392
330
|
complete: {
|
|
393
331
|
description: "Mark todo as complete",
|
|
394
|
-
input: z.object({
|
|
395
|
-
|
|
396
|
-
}),
|
|
397
|
-
guard: {
|
|
398
|
-
roles: ["member", "admin"],
|
|
399
|
-
record: { completed: { equals: false } },
|
|
400
|
-
},
|
|
332
|
+
input: z.object({ completedAt: z.string().datetime().optional() }),
|
|
333
|
+
guard: { roles: ["member", "admin"], record: { completed: { equals: false } } },
|
|
401
334
|
execute: async ({ db, record, ctx, input }) => {
|
|
402
|
-
// Inline handler
|
|
403
335
|
await db.update(todos).set({ completed: true }).where(eq(todos.id, record.id));
|
|
404
336
|
return { success: true };
|
|
405
337
|
},
|
|
406
338
|
sideEffects: "sync",
|
|
407
339
|
},
|
|
408
|
-
|
|
409
340
|
archive: {
|
|
410
341
|
description: "Archive a todo",
|
|
411
342
|
input: z.object({}),
|
|
412
343
|
guard: { roles: ["admin"] },
|
|
413
|
-
handler: "./handlers/archive",
|
|
344
|
+
handler: "./handlers/archive",
|
|
414
345
|
},
|
|
415
346
|
});
|
|
416
347
|
```
|
|
417
348
|
|
|
418
|
-
**
|
|
419
|
-
|
|
420
|
-
### Record-based vs Standalone Actions
|
|
421
|
-
|
|
422
|
-
**Record-based** (default) — operates on a specific record via `:id`:
|
|
423
|
-
```typescript
|
|
424
|
-
approve: {
|
|
425
|
-
description: "Approve an invoice",
|
|
426
|
-
input: z.object({ notes: z.string().optional() }),
|
|
427
|
-
guard: { roles: ["admin"] },
|
|
428
|
-
handler: "./handlers/approve",
|
|
429
|
-
}
|
|
430
|
-
// → POST /api/v1/invoices/:id/approve
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
**Standalone** — custom endpoint not tied to a record:
|
|
434
|
-
```typescript
|
|
435
|
-
chat: {
|
|
436
|
-
standalone: true,
|
|
437
|
-
path: "/chat",
|
|
438
|
-
method: "POST",
|
|
439
|
-
responseType: "stream",
|
|
440
|
-
input: z.object({ message: z.string() }),
|
|
441
|
-
guard: { roles: ["member"] },
|
|
442
|
-
handler: "./handlers/chat",
|
|
443
|
-
}
|
|
444
|
-
// → POST /api/v1/todos/chat
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### Action Options
|
|
349
|
+
**Record-based** (default): `POST /api/v1/{resource}/:id/{action}`
|
|
350
|
+
**Standalone**: `standalone: true`, custom `path` and `method`
|
|
448
351
|
|
|
449
|
-
|
|
450
|
-
|--------|--------|-------------|
|
|
451
|
-
| `standalone` | `true` | Not tied to a record |
|
|
452
|
-
| `method` | `GET`, `POST`, `PUT`, `PATCH`, `DELETE` | HTTP method (default: POST) |
|
|
453
|
-
| `responseType` | `json`, `stream`, `file` | Response format |
|
|
454
|
-
| `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)
|
|
455
353
|
|
|
456
354
|
---
|
|
457
355
|
|
|
458
|
-
|
|
356
|
+
## API Reference
|
|
459
357
|
|
|
460
|
-
|
|
358
|
+
### CRUD Operations
|
|
461
359
|
|
|
462
360
|
| Method | Endpoint | Description |
|
|
463
361
|
|--------|----------|-------------|
|
|
@@ -468,276 +366,266 @@ chat: {
|
|
|
468
366
|
| `DELETE` | `/api/v1/{resource}/:id` | Delete record |
|
|
469
367
|
| `PUT` | `/api/v1/{resource}/:id` | Upsert (if enabled) |
|
|
470
368
|
|
|
471
|
-
|
|
369
|
+
### Batch Operations
|
|
472
370
|
|
|
473
|
-
|
|
|
474
|
-
|--------|----------|-------------|
|
|
475
|
-
| `POST` | `/api/v1/{resource}/batch` | Batch create |
|
|
476
|
-
| `PATCH` | `/api/v1/{resource}/batch` | Batch update |
|
|
477
|
-
| `DELETE` | `/api/v1/{resource}/batch` | Batch delete |
|
|
478
|
-
| `PUT` | `/api/v1/{resource}/batch` | Batch upsert (if enabled) |
|
|
479
|
-
|
|
480
|
-
```json
|
|
481
|
-
// Request
|
|
482
|
-
{ "records": [{ "title": "A" }, { "title": "B" }], "options": { "atomic": false } }
|
|
483
|
-
|
|
484
|
-
// Response
|
|
485
|
-
{ "success": [/* ... */], "errors": [/* ... */], "meta": { "total": 2, "succeeded": 2, "failed": 0 } }
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
## Query Parameters
|
|
371
|
+
`POST|PATCH|DELETE|PUT /api/v1/{resource}/batch` with `{ "records": [...], "options": { "atomic": false } }`
|
|
489
372
|
|
|
490
|
-
###
|
|
373
|
+
### Query Parameters
|
|
491
374
|
|
|
492
|
-
|
|
|
493
|
-
|
|
494
|
-
|
|
|
495
|
-
|
|
|
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) |
|
|
496
383
|
|
|
497
|
-
|
|
384
|
+
Filter operators: `.gt`, `.gte`, `.lt`, `.lte`, `.ne`, `.like`, `.in`
|
|
498
385
|
|
|
499
|
-
|
|
500
|
-
|----------|---------|-----|
|
|
501
|
-
| (none) | `?status=active` | `status = 'active'` |
|
|
502
|
-
| `.gt` | `?amount.gt=100` | `amount > 100` |
|
|
503
|
-
| `.gte` | `?amount.gte=100` | `amount >= 100` |
|
|
504
|
-
| `.lt` | `?amount.lt=1000` | `amount < 1000` |
|
|
505
|
-
| `.lte` | `?amount.lte=1000` | `amount <= 1000` |
|
|
506
|
-
| `.ne` | `?status.ne=cancelled` | `status != 'cancelled'` |
|
|
507
|
-
| `.like` | `?title.like=urgent` | `title LIKE '%urgent%'` |
|
|
508
|
-
| `.in` | `?status.in=draft,pending` | `status IN ('draft','pending')` |
|
|
386
|
+
### Response Format
|
|
509
387
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
?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
|
+
}
|
|
516
393
|
```
|
|
517
394
|
|
|
518
|
-
|
|
395
|
+
### Error Responses
|
|
519
396
|
|
|
520
|
-
|
|
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 |
|
|
521
403
|
|
|
522
|
-
|
|
523
|
-
?fields=id,name,status # Return only these columns (LIST + GET routes)
|
|
524
|
-
```
|
|
404
|
+
---
|
|
525
405
|
|
|
526
|
-
|
|
406
|
+
## Database Dialects
|
|
527
407
|
|
|
528
|
-
|
|
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` |
|
|
529
413
|
|
|
530
|
-
|
|
531
|
-
?total=true # Include pagination.total (LIST + VIEW routes)
|
|
532
|
-
```
|
|
414
|
+
---
|
|
533
415
|
|
|
534
|
-
|
|
416
|
+
# PART 2: THE QUICKBACK STACK
|
|
535
417
|
|
|
536
|
-
|
|
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.
|
|
537
419
|
|
|
538
420
|
```
|
|
539
|
-
|
|
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
|
+
└─────────────────────────────────────────────────────────┘
|
|
540
433
|
```
|
|
541
434
|
|
|
542
|
-
|
|
435
|
+
## Authentication (Better Auth)
|
|
543
436
|
|
|
544
|
-
|
|
437
|
+
Built on Better Auth with multi-tenant organization support.
|
|
545
438
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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"),
|
|
555
449
|
}
|
|
556
450
|
```
|
|
557
451
|
|
|
558
|
-
`
|
|
452
|
+
Docs: `quickback docs stack/auth/using-auth` | https://docs.quickback.dev/stack/auth
|
|
559
453
|
|
|
560
|
-
|
|
454
|
+
## Database (D1)
|
|
455
|
+
|
|
456
|
+
SQLite at the edge with zero configuration. Multi-database pattern for independent scaling:
|
|
561
457
|
|
|
562
|
-
|
|
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 |
|
|
563
464
|
|
|
564
|
-
|
|
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
|
|
565
470
|
|
|
566
471
|
```typescript
|
|
567
|
-
// quickback.config.ts
|
|
568
472
|
providers: {
|
|
569
473
|
database: defineDatabase("cloudflare-d1", {
|
|
570
474
|
splitDatabases: true,
|
|
571
|
-
webhooksBinding: "WEBHOOKS_DB",
|
|
572
475
|
}),
|
|
573
476
|
}
|
|
574
477
|
```
|
|
575
478
|
|
|
576
|
-
|
|
479
|
+
Docs: `quickback docs stack/database/d1` | https://docs.quickback.dev/stack/database/d1
|
|
577
480
|
|
|
578
|
-
|
|
579
|
-
import { onWebhookEvent } from './lib/webhooks';
|
|
481
|
+
## File Storage (R2)
|
|
580
482
|
|
|
581
|
-
|
|
582
|
-
const { data, env } = ctx;
|
|
583
|
-
await createSubscription({ ... });
|
|
584
|
-
});
|
|
585
|
-
```
|
|
483
|
+
S3-compatible object storage with built-in access control.
|
|
586
484
|
|
|
587
|
-
|
|
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/*`
|
|
588
490
|
|
|
589
|
-
|
|
590
|
-
import { emitWebhookEvent } from './lib/webhooks';
|
|
491
|
+
Docs: `quickback docs stack/storage/r2` | https://docs.quickback.dev/stack/storage/r2
|
|
591
492
|
|
|
592
|
-
|
|
593
|
-
'user.created',
|
|
594
|
-
{ id: user.id, email: user.email },
|
|
595
|
-
{ organizationId: orgId },
|
|
596
|
-
env
|
|
597
|
-
);
|
|
598
|
-
```
|
|
493
|
+
## KV Storage
|
|
599
494
|
|
|
600
|
-
|
|
495
|
+
Distributed key-value store optimized for reads (300+ edge locations, sub-millisecond).
|
|
601
496
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
| `subscription.created/updated/cancelled/renewed` | Subscription lifecycle |
|
|
606
|
-
| `organization.created/updated/deleted` | Organization lifecycle |
|
|
607
|
-
| `organization.member_added/removed` | Membership changes |
|
|
608
|
-
| `file.uploaded/deleted` | File storage |
|
|
497
|
+
- Use cases: sessions, caching, rate limiting, feature flags
|
|
498
|
+
- Eventually consistent (60 second propagation)
|
|
499
|
+
- TTL/expiration support
|
|
609
500
|
|
|
610
|
-
|
|
501
|
+
Docs: `quickback docs stack/storage/kv` | https://docs.quickback.dev/stack/storage/kv
|
|
611
502
|
|
|
612
|
-
|
|
503
|
+
## Realtime (Durable Objects)
|
|
613
504
|
|
|
614
|
-
|
|
505
|
+
WebSocket connections for live updates via organization-scoped Durable Objects.
|
|
615
506
|
|
|
616
|
-
|
|
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`
|
|
617
513
|
|
|
618
|
-
|
|
619
|
-
|---------|-------------|
|
|
620
|
-
| `quickback create <template> <name>` | Create project from template |
|
|
621
|
-
| `quickback compile` | Compile definitions to output |
|
|
622
|
-
| `quickback login` | Authenticate for Pro templates |
|
|
623
|
-
| `quickback logout` | Clear stored credentials |
|
|
624
|
-
| `quickback whoami` | Show current auth status |
|
|
514
|
+
Docs: `quickback docs stack/realtime/durable-objects` | https://docs.quickback.dev/stack/realtime
|
|
625
515
|
|
|
626
|
-
##
|
|
516
|
+
## Queues (Background Processing)
|
|
627
517
|
|
|
628
|
-
|
|
629
|
-
|----------|-------|--------|
|
|
630
|
-
| `cloudflare` | Cloudflare Workers + D1 + Better Auth | Free |
|
|
631
|
-
| `bun` | Bun + SQLite + Better Auth | Free |
|
|
632
|
-
| `turso` | Turso/LibSQL + Better Auth | Pro |
|
|
518
|
+
Cloudflare Queues for reliable async job processing.
|
|
633
519
|
|
|
634
|
-
|
|
520
|
+
| Queue | Purpose |
|
|
521
|
+
|-------|---------|
|
|
522
|
+
| `EMBEDDINGS_QUEUE` | Auto-generate embeddings |
|
|
523
|
+
| `WEBHOOKS_QUEUE` | Webhook delivery |
|
|
524
|
+
| Custom queues | Your background jobs |
|
|
635
525
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
| `quickback claude install --global` | Install globally |
|
|
640
|
-
| `quickback claude install --local` | Install for current project |
|
|
641
|
-
| `quickback claude update` | Update to latest skill version |
|
|
642
|
-
| `quickback claude remove` | Remove the skill |
|
|
643
|
-
| `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
|
|
644
529
|
|
|
645
|
-
|
|
530
|
+
Docs: `quickback docs stack/queues/using-queues` | https://docs.quickback.dev/stack/queues
|
|
646
531
|
|
|
647
|
-
|
|
532
|
+
## Vector & Embeddings (Workers AI)
|
|
648
533
|
|
|
649
|
-
|
|
650
|
-
# 1. Create project
|
|
651
|
-
quickback create cloudflare my-app
|
|
534
|
+
Auto-generated embeddings for similarity search and classification.
|
|
652
535
|
|
|
653
|
-
|
|
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`
|
|
654
542
|
|
|
655
|
-
|
|
656
|
-
quickback compile
|
|
543
|
+
Docs: `quickback docs stack/vector/embeddings` | https://docs.quickback.dev/stack/vector
|
|
657
544
|
|
|
658
|
-
|
|
659
|
-
cd dist
|
|
660
|
-
npm install
|
|
661
|
-
npm run db:generate
|
|
662
|
-
npm run dev
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
---
|
|
545
|
+
## Webhooks
|
|
666
546
|
|
|
667
|
-
|
|
547
|
+
### Inbound (Receiving)
|
|
668
548
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
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`
|
|
672
550
|
|
|
673
551
|
```typescript
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
},
|
|
552
|
+
import { onWebhookEvent } from './lib/webhooks';
|
|
553
|
+
|
|
554
|
+
onWebhookEvent('stripe:checkout.session.completed', async (ctx) => {
|
|
555
|
+
const { data, env } = ctx;
|
|
556
|
+
await createSubscription({ ... });
|
|
680
557
|
});
|
|
681
558
|
```
|
|
682
559
|
|
|
683
|
-
|
|
560
|
+
### Outbound (Sending)
|
|
684
561
|
|
|
685
|
-
|
|
562
|
+
Emit signed events when data changes. HMAC-SHA256 signing, async delivery via queue with exponential backoff retry.
|
|
686
563
|
|
|
687
564
|
```typescript
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
database: defineDatabase("supabase"),
|
|
692
|
-
auth: defineAuth("supabase"),
|
|
693
|
-
},
|
|
694
|
-
});
|
|
565
|
+
import { emitWebhookEvent } from './lib/webhooks';
|
|
566
|
+
|
|
567
|
+
await emitWebhookEvent('user.created', { id: user.id, email: user.email }, { organizationId: orgId }, env);
|
|
695
568
|
```
|
|
696
569
|
|
|
697
|
-
|
|
570
|
+
Event patterns: `user.*`, `subscription.*`, `organization.*`, `file.*`, `*`
|
|
571
|
+
Endpoint management: `/webhooks/v1/endpoints`
|
|
698
572
|
|
|
699
|
-
|
|
573
|
+
Docs: `quickback docs stack/webhooks/outbound` | https://docs.quickback.dev/stack/webhooks
|
|
700
574
|
|
|
701
575
|
---
|
|
702
576
|
|
|
703
|
-
#
|
|
577
|
+
# CLI Commands
|
|
704
578
|
|
|
705
|
-
|
|
|
706
|
-
|
|
707
|
-
| `
|
|
708
|
-
| `
|
|
709
|
-
| `
|
|
710
|
-
| `
|
|
711
|
-
| `
|
|
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 |
|
|
712
589
|
|
|
713
|
-
|
|
590
|
+
## Available Templates
|
|
714
591
|
|
|
715
|
-
|
|
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 |
|
|
716
597
|
|
|
717
|
-
|
|
598
|
+
## Development Workflow
|
|
718
599
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
600
|
+
```bash
|
|
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
|
|
605
|
+
```
|
|
724
606
|
|
|
725
607
|
---
|
|
726
608
|
|
|
727
|
-
# Documentation
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
- [
|
|
732
|
-
- [
|
|
733
|
-
- [
|
|
734
|
-
- [
|
|
735
|
-
- [
|
|
736
|
-
- [
|
|
737
|
-
- [
|
|
738
|
-
- [
|
|
739
|
-
- [
|
|
740
|
-
- [
|
|
741
|
-
- [
|
|
742
|
-
- [
|
|
743
|
-
- [
|
|
609
|
+
# Online Documentation
|
|
610
|
+
|
|
611
|
+
- [Getting Started](https://docs.quickback.dev/compiler/getting-started)
|
|
612
|
+
- [Concepts](https://docs.quickback.dev/compiler/definitions/concepts)
|
|
613
|
+
- [Database Schema](https://docs.quickback.dev/compiler/definitions/schema)
|
|
614
|
+
- [Firewall](https://docs.quickback.dev/compiler/definitions/firewall)
|
|
615
|
+
- [Access](https://docs.quickback.dev/compiler/definitions/access)
|
|
616
|
+
- [Guards](https://docs.quickback.dev/compiler/definitions/guards)
|
|
617
|
+
- [Masking](https://docs.quickback.dev/compiler/definitions/masking)
|
|
618
|
+
- [Views](https://docs.quickback.dev/compiler/definitions/views)
|
|
619
|
+
- [Actions](https://docs.quickback.dev/compiler/definitions/actions)
|
|
620
|
+
- [CRUD API](https://docs.quickback.dev/compiler/using-the-api/crud)
|
|
621
|
+
- [Query Parameters](https://docs.quickback.dev/compiler/using-the-api/query-params)
|
|
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)
|
|
630
|
+
- [CMS Overview](https://docs.quickback.dev/cms)
|
|
631
|
+
- [CMS Record Layouts](https://docs.quickback.dev/cms/record-layouts)
|
|
@@ -19,6 +19,7 @@ You deeply understand:
|
|
|
19
19
|
- **Actions**: Custom endpoints using `defineActions()` with Zod schemas
|
|
20
20
|
- **Views**: Column-level security with named projections
|
|
21
21
|
- **Validation**: Field-level validation rules
|
|
22
|
+
- **Layouts**: CMS record page field grouping with sections, columns, and collapsed state
|
|
22
23
|
|
|
23
24
|
## When Invoked
|
|
24
25
|
|
|
@@ -193,10 +194,24 @@ Before finishing, verify:
|
|
|
193
194
|
- [ ] Masking for any PII fields
|
|
194
195
|
- [ ] Views for different visibility levels (if needed)
|
|
195
196
|
- [ ] Validation rules for constrained fields (if needed)
|
|
197
|
+
- [ ] Layouts for CMS record page field grouping (if needed)
|
|
196
198
|
- [ ] No audit fields in schema (auto-injected)
|
|
197
199
|
- [ ] Using `defineTable()` combined mode (not separate schema.ts + resource.ts)
|
|
198
200
|
- [ ] Actions use `defineActions()` with Zod schemas (not JSON schema)
|
|
199
201
|
|
|
202
|
+
## Accessing Documentation
|
|
203
|
+
|
|
204
|
+
When you need to look up Quickback docs, use the CLI — it bundles all docs offline:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
quickback docs # List all available topics
|
|
208
|
+
quickback docs <topic> # Show docs for a specific topic
|
|
209
|
+
quickback docs firewall # Example: firewall docs
|
|
210
|
+
quickback docs cms/record-layouts # Example: CMS record layouts
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Full online docs: https://docs.quickback.dev
|
|
214
|
+
|
|
200
215
|
## Response Style
|
|
201
216
|
|
|
202
217
|
- Be direct and practical
|