@kardoe/quickback 0.4.4 → 0.5.2
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/commands/compile.d.ts.map +1 -1
- package/dist/commands/compile.js +8 -6
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +165 -136
- package/dist/commands/create.js.map +1 -1
- package/dist/docs/content.d.ts +1 -1
- package/dist/docs/content.d.ts.map +1 -1
- package/dist/docs/content.js +2 -89
- package/dist/docs/content.js.map +1 -1
- package/dist/index.js +29 -28
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +22 -4
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +52 -4
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/async-utils.d.ts +6 -0
- package/dist/lib/async-utils.d.ts.map +1 -0
- package/dist/lib/async-utils.js +26 -0
- package/dist/lib/async-utils.js.map +1 -0
- package/dist/lib/file-loader.d.ts +7 -4
- package/dist/lib/file-loader.d.ts.map +1 -1
- package/dist/lib/file-loader.js +165 -112
- package/dist/lib/file-loader.js.map +1 -1
- package/dist/lib/file-writer.d.ts +4 -7
- package/dist/lib/file-writer.d.ts.map +1 -1
- package/dist/lib/file-writer.js +42 -32
- package/dist/lib/file-writer.js.map +1 -1
- package/dist/templates/registry.d.ts.map +1 -1
- package/dist/templates/registry.js +4 -5
- package/dist/templates/registry.js.map +1 -1
- package/package.json +12 -5
- package/src/skill/SKILL.md +383 -133
- package/src/skill/agents/quickback-specialist/AGENT.md +110 -13
package/src/skill/SKILL.md
CHANGED
|
@@ -4,28 +4,17 @@ 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: Backend Compiler for Secure APIs
|
|
8
8
|
|
|
9
|
-
Quickback
|
|
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.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
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
12
|
|
|
13
|
-
##
|
|
13
|
+
## How It Works
|
|
14
14
|
|
|
15
|
-
**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# Install the CLI
|
|
19
|
-
npm install -g @kardoe/quickback
|
|
20
|
-
|
|
21
|
-
# Create a new project (scaffolds correct structure)
|
|
22
|
-
quickback create cloudflare my-app
|
|
23
|
-
|
|
24
|
-
# Or for other templates
|
|
25
|
-
quickback create bun my-app
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
This creates the correct project structure with all configuration files.
|
|
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
|
|
29
18
|
|
|
30
19
|
## Architecture
|
|
31
20
|
|
|
@@ -34,33 +23,48 @@ This creates the correct project structure with all configuration files.
|
|
|
34
23
|
│ YOU DEFINE (in TypeScript) │
|
|
35
24
|
│ • Drizzle schema (your data models) │
|
|
36
25
|
│ • Security layers (Firewall, Access, Guards, Masking) │
|
|
37
|
-
│ •
|
|
26
|
+
│ • Views (column-level projections) │
|
|
38
27
|
│ • Actions (your business operations) │
|
|
39
28
|
├─────────────────────────────────────────────────────────┤
|
|
40
|
-
│ QUICKBACK COMPILES TO
|
|
29
|
+
│ QUICKBACK COMPILES TO │
|
|
41
30
|
│ • Database migrations (via Drizzle) │
|
|
42
|
-
│ • API route handlers (
|
|
31
|
+
│ • API route handlers (Hono) │
|
|
43
32
|
│ • Typed client SDK for your frontend │
|
|
44
33
|
│ • AI tool definitions │
|
|
45
34
|
│ • OpenAPI specification │
|
|
46
|
-
│ • Compliance manifest │
|
|
47
35
|
└─────────────────────────────────────────────────────────┘
|
|
48
36
|
```
|
|
49
37
|
|
|
50
|
-
## Security
|
|
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
|
+
```
|
|
51
45
|
|
|
52
|
-
|
|
|
53
|
-
|
|
54
|
-
| **
|
|
55
|
-
| **
|
|
56
|
-
| **
|
|
57
|
-
| **
|
|
58
|
-
| **Workflow state machines** | **Actions** — Typed state transitions with preconditions |
|
|
59
|
-
| **AI-safe mutation boundaries** | **Actions** — Constrained, typed tools an AI can safely call |
|
|
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 | — |
|
|
60
52
|
|
|
61
53
|
---
|
|
62
54
|
|
|
63
|
-
# Project
|
|
55
|
+
# Project Structure
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
my-app/
|
|
59
|
+
├── quickback/
|
|
60
|
+
│ ├── quickback.config.ts
|
|
61
|
+
│ └── features/
|
|
62
|
+
│ └── {feature-name}/
|
|
63
|
+
│ ├── {table}.ts # Schema + security (defineTable)
|
|
64
|
+
│ ├── actions.ts # Custom actions (optional)
|
|
65
|
+
│ └── handlers/ # Action handlers (optional)
|
|
66
|
+
└── ...
|
|
67
|
+
```
|
|
64
68
|
|
|
65
69
|
## quickback.config.ts
|
|
66
70
|
|
|
@@ -86,7 +90,7 @@ Quickback automatically injects these fields into every table:
|
|
|
86
90
|
- `createdAt`, `modifiedAt`, `deletedAt` (timestamps)
|
|
87
91
|
- `createdBy`, `modifiedBy`, `deletedBy` (user IDs)
|
|
88
92
|
|
|
89
|
-
Do NOT add these to your schema
|
|
93
|
+
**Do NOT add these to your schema files.** They are auto-injected by the compiler.
|
|
90
94
|
|
|
91
95
|
---
|
|
92
96
|
|
|
@@ -105,22 +109,29 @@ import { defineTable } from "@quickback/compiler";
|
|
|
105
109
|
export const todos = sqliteTable("todos", {
|
|
106
110
|
id: integer("id").primaryKey(),
|
|
107
111
|
title: text("title").notNull(),
|
|
112
|
+
description: text("description"),
|
|
108
113
|
completed: integer("completed", { mode: "boolean" }).default(false),
|
|
109
114
|
userId: text("user_id").notNull(),
|
|
110
115
|
organizationId: text("organization_id").notNull(),
|
|
111
116
|
});
|
|
112
117
|
|
|
113
118
|
export default defineTable(todos, {
|
|
114
|
-
crud: ["create", "read", "update", "delete"],
|
|
115
|
-
|
|
116
119
|
firewall: {
|
|
117
|
-
organization: {
|
|
118
|
-
owner: {
|
|
120
|
+
organization: {}, // Auto-detects 'organizationId' column
|
|
121
|
+
owner: {}, // Auto-detects 'userId' column
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
crud: {
|
|
125
|
+
list: { access: { roles: ["member", "admin"] } },
|
|
126
|
+
get: { access: { roles: ["member", "admin"] } },
|
|
127
|
+
create: { access: { roles: ["member", "admin"] } },
|
|
128
|
+
update: { access: { roles: ["admin"] } },
|
|
129
|
+
delete: { access: { roles: ["admin"] }, mode: "soft" },
|
|
119
130
|
},
|
|
120
131
|
|
|
121
132
|
guards: {
|
|
122
|
-
createable: ["title", "completed"],
|
|
123
|
-
updatable: ["title", "completed"],
|
|
133
|
+
createable: ["title", "description", "completed"],
|
|
134
|
+
updatable: ["title", "description", "completed"],
|
|
124
135
|
},
|
|
125
136
|
|
|
126
137
|
masking: {
|
|
@@ -129,13 +140,25 @@ export default defineTable(todos, {
|
|
|
129
140
|
});
|
|
130
141
|
```
|
|
131
142
|
|
|
143
|
+
## Internal/Junction Tables (No API)
|
|
144
|
+
|
|
145
|
+
Tables without a `defineTable()` default export get no API routes:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// No default export = no API routes generated
|
|
149
|
+
export const roomAmenities = sqliteTable('room_amenities', {
|
|
150
|
+
roomId: text('room_id').notNull(),
|
|
151
|
+
amenityId: text('amenity_id').notNull(),
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
132
155
|
---
|
|
133
156
|
|
|
134
157
|
# Security Pillars
|
|
135
158
|
|
|
136
|
-
## 1. Firewall
|
|
159
|
+
## 1. Firewall — Data Isolation
|
|
137
160
|
|
|
138
|
-
Compiles WHERE clauses to isolate data by ownership.
|
|
161
|
+
Compiles WHERE clauses to isolate data by ownership. Auto-detects column names.
|
|
139
162
|
|
|
140
163
|
```typescript
|
|
141
164
|
firewall: {
|
|
@@ -147,6 +170,13 @@ firewall: {
|
|
|
147
170
|
}
|
|
148
171
|
```
|
|
149
172
|
|
|
173
|
+
### Auto-Detection
|
|
174
|
+
|
|
175
|
+
Quickback finds columns by convention:
|
|
176
|
+
- `organization` → looks for `organizationId` or `organization_id`
|
|
177
|
+
- `owner` → looks for `userId` or `user_id`
|
|
178
|
+
- `team` → looks for `teamId` or `team_id`
|
|
179
|
+
|
|
150
180
|
### Column Overrides
|
|
151
181
|
|
|
152
182
|
```typescript
|
|
@@ -158,9 +188,16 @@ firewall: {
|
|
|
158
188
|
}
|
|
159
189
|
```
|
|
160
190
|
|
|
161
|
-
|
|
191
|
+
### Owner Modes
|
|
192
|
+
|
|
193
|
+
| Mode | Behavior |
|
|
194
|
+
|------|----------|
|
|
195
|
+
| (default) | Strict — only own records |
|
|
196
|
+
| `optional` | Own records + records with NULL owner (admin pattern) |
|
|
162
197
|
|
|
163
|
-
|
|
198
|
+
## 2. Access — Permission Checks
|
|
199
|
+
|
|
200
|
+
Role-based and record-based access control. Deny by default.
|
|
164
201
|
|
|
165
202
|
```typescript
|
|
166
203
|
crud: {
|
|
@@ -179,9 +216,28 @@ crud: {
|
|
|
179
216
|
}
|
|
180
217
|
```
|
|
181
218
|
|
|
182
|
-
|
|
219
|
+
### Record Conditions
|
|
220
|
+
|
|
221
|
+
| Operator | Example |
|
|
222
|
+
|----------|---------|
|
|
223
|
+
| `equals` | `{ status: { equals: 'active' } }` |
|
|
224
|
+
| `notEquals` | `{ status: { notEquals: 'archived' } }` |
|
|
225
|
+
| `in` | `{ status: { in: ['active', 'pending'] } }` |
|
|
226
|
+
| `notIn` | `{ status: { notIn: ['deleted'] } }` |
|
|
227
|
+
| `greaterThan` | `{ amount: { greaterThan: 0 } }` |
|
|
228
|
+
| `lessThan` | `{ amount: { lessThan: 10000 } }` |
|
|
229
|
+
|
|
230
|
+
### Context Variables
|
|
183
231
|
|
|
184
|
-
|
|
232
|
+
| Variable | Type | Description |
|
|
233
|
+
|----------|------|-------------|
|
|
234
|
+
| `$ctx.userId` | string | Current user's ID |
|
|
235
|
+
| `$ctx.activeOrgId` | string | Active organization ID |
|
|
236
|
+
| `$ctx.activeTeamId` | string \| null | Active team ID |
|
|
237
|
+
| `$ctx.roles` | string[] | User's roles in active org |
|
|
238
|
+
| `$ctx.isAnonymous` | boolean | Anonymous user flag |
|
|
239
|
+
|
|
240
|
+
## 3. Guards — Field Protection
|
|
185
241
|
|
|
186
242
|
Controls which fields can be modified.
|
|
187
243
|
|
|
@@ -190,16 +246,38 @@ guards: {
|
|
|
190
246
|
createable: ['name', 'description'], // Allowed on POST
|
|
191
247
|
updatable: ['description'], // Allowed on PATCH
|
|
192
248
|
immutable: ['invoiceNumber'], // Set once, never change
|
|
193
|
-
protected: { status: ['approve'] }, // Only via actions
|
|
249
|
+
protected: { status: ['approve'] }, // Only via named actions
|
|
194
250
|
}
|
|
195
251
|
```
|
|
196
252
|
|
|
253
|
+
**Compile-time validation**:
|
|
254
|
+
- Field can't be in both `createable` and `protected`
|
|
255
|
+
- Field can't be in both `updatable` and `immutable`
|
|
256
|
+
- All referenced fields must exist in the schema
|
|
257
|
+
|
|
197
258
|
**System-managed fields** (always protected):
|
|
198
|
-
- `createdAt`, `createdBy`
|
|
199
|
-
- `modifiedAt`, `modifiedBy`
|
|
200
|
-
- `deletedAt`, `deletedBy`
|
|
259
|
+
- `createdAt`, `createdBy` — Set on INSERT
|
|
260
|
+
- `modifiedAt`, `modifiedBy` — Set on INSERT/UPDATE
|
|
261
|
+
- `deletedAt`, `deletedBy` — Set on soft DELETE
|
|
262
|
+
|
|
263
|
+
### PUT / Upsert
|
|
264
|
+
|
|
265
|
+
PUT is only available when BOTH conditions are met:
|
|
266
|
+
- `generateId: false` in database config
|
|
267
|
+
- `guards: false` (disables field protection)
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
export default defineTable(external_orders, {
|
|
271
|
+
guards: false,
|
|
272
|
+
crud: {
|
|
273
|
+
put: { access: { roles: ['sync-service'] } },
|
|
274
|
+
get: {},
|
|
275
|
+
list: {},
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
```
|
|
201
279
|
|
|
202
|
-
## 4. Masking
|
|
280
|
+
## 4. Masking — PII Redaction
|
|
203
281
|
|
|
204
282
|
```typescript
|
|
205
283
|
masking: {
|
|
@@ -214,41 +292,157 @@ masking: {
|
|
|
214
292
|
}
|
|
215
293
|
```
|
|
216
294
|
|
|
217
|
-
|
|
295
|
+
### Mask Types
|
|
296
|
+
|
|
297
|
+
| Type | Output |
|
|
298
|
+
|------|--------|
|
|
299
|
+
| `email` | `p***@e****.com` |
|
|
300
|
+
| `phone` | `***-***-4567` |
|
|
301
|
+
| `ssn` | `***-**-6789` |
|
|
302
|
+
| `creditCard` | `****-****-****-1234` |
|
|
303
|
+
| `name` | `J*** D**` |
|
|
304
|
+
| `redact` | `[REDACTED]` |
|
|
305
|
+
| `custom` | Your function |
|
|
306
|
+
|
|
307
|
+
### Auto-Detection
|
|
308
|
+
|
|
309
|
+
The compiler warns if sensitive columns aren't explicitly configured:
|
|
310
|
+
|
|
311
|
+
| Column Pattern | Default Mask |
|
|
312
|
+
|---------------|-------------|
|
|
313
|
+
| `email` | `email` |
|
|
314
|
+
| `phone`, `mobile`, `fax` | `phone` |
|
|
315
|
+
| `ssn`, `socialsecurity` | `ssn` |
|
|
316
|
+
| `creditcard`, `cc` | `creditCard` |
|
|
317
|
+
| `password`, `secret`, `token` | `redact` |
|
|
218
318
|
|
|
219
319
|
---
|
|
220
320
|
|
|
221
|
-
#
|
|
321
|
+
# Views — Column-Level Security
|
|
222
322
|
|
|
223
|
-
|
|
323
|
+
Views are named projections that control which fields are visible based on role.
|
|
224
324
|
|
|
225
325
|
```typescript
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
description: "Mark todo as complete",
|
|
234
|
-
input: {
|
|
235
|
-
type: "object",
|
|
236
|
-
properties: {
|
|
237
|
-
completedAt: { type: "string", format: "datetime", optional: true },
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
guard: {
|
|
241
|
-
roles: ["owner", "member", "admin"],
|
|
242
|
-
record: { completed: { equals: false } },
|
|
243
|
-
},
|
|
244
|
-
sideEffects: "sync",
|
|
326
|
+
export default defineTable(customers, {
|
|
327
|
+
// ... firewall, guards, etc.
|
|
328
|
+
|
|
329
|
+
views: {
|
|
330
|
+
summary: {
|
|
331
|
+
fields: ['id', 'name', 'email'],
|
|
332
|
+
access: { roles: ['member', 'admin'] },
|
|
245
333
|
},
|
|
334
|
+
full: {
|
|
335
|
+
fields: ['id', 'name', 'email', 'phone', 'ssn', 'address'],
|
|
336
|
+
access: { roles: ['admin'] },
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**Generated endpoints**:
|
|
343
|
+
- `GET /api/v1/customers/views/summary` — returns only id, name, email
|
|
344
|
+
- `GET /api/v1/customers/views/full` — returns all fields (admin only)
|
|
345
|
+
|
|
346
|
+
Views support the same query parameters as list (filtering, sorting, pagination). Masking still applies to returned fields.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
# Validation
|
|
351
|
+
|
|
352
|
+
Field-level validation rules compiled into the API:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
export default defineTable(rooms, {
|
|
356
|
+
// ... other config
|
|
357
|
+
|
|
358
|
+
validation: {
|
|
359
|
+
name: { minLength: 1, maxLength: 100 },
|
|
360
|
+
capacity: { min: 1, max: 1000 },
|
|
361
|
+
roomType: { enum: ['meeting', 'conference', 'breakout'] },
|
|
362
|
+
email: { email: true },
|
|
363
|
+
website: { url: true },
|
|
364
|
+
code: { pattern: '^[A-Z]{3}$' },
|
|
246
365
|
},
|
|
247
|
-
};
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
# Actions — Custom Business Logic
|
|
372
|
+
|
|
373
|
+
Actions are custom API endpoints for business logic beyond CRUD. Defined in a separate `actions.ts` file using `defineActions()`.
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// quickback/features/todos/actions.ts
|
|
377
|
+
import { todos } from './todos';
|
|
378
|
+
import { defineActions } from '@quickback/compiler';
|
|
379
|
+
import { z } from 'zod';
|
|
380
|
+
|
|
381
|
+
export default defineActions(todos, {
|
|
382
|
+
complete: {
|
|
383
|
+
description: "Mark todo as complete",
|
|
384
|
+
input: z.object({
|
|
385
|
+
completedAt: z.string().datetime().optional(),
|
|
386
|
+
}),
|
|
387
|
+
guard: {
|
|
388
|
+
roles: ["member", "admin"],
|
|
389
|
+
record: { completed: { equals: false } },
|
|
390
|
+
},
|
|
391
|
+
execute: async ({ db, record, ctx, input }) => {
|
|
392
|
+
// Inline handler
|
|
393
|
+
await db.update(todos).set({ completed: true }).where(eq(todos.id, record.id));
|
|
394
|
+
return { success: true };
|
|
395
|
+
},
|
|
396
|
+
sideEffects: "sync",
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
archive: {
|
|
400
|
+
description: "Archive a todo",
|
|
401
|
+
input: z.object({}),
|
|
402
|
+
guard: { roles: ["admin"] },
|
|
403
|
+
handler: "./handlers/archive", // OR: external file handler
|
|
404
|
+
},
|
|
405
|
+
});
|
|
248
406
|
```
|
|
249
407
|
|
|
250
408
|
**Generated Route**: `POST /api/v1/todos/:id/complete`
|
|
251
409
|
|
|
410
|
+
### Record-based vs Standalone Actions
|
|
411
|
+
|
|
412
|
+
**Record-based** (default) — operates on a specific record via `:id`:
|
|
413
|
+
```typescript
|
|
414
|
+
approve: {
|
|
415
|
+
description: "Approve an invoice",
|
|
416
|
+
input: z.object({ notes: z.string().optional() }),
|
|
417
|
+
guard: { roles: ["admin"] },
|
|
418
|
+
handler: "./handlers/approve",
|
|
419
|
+
}
|
|
420
|
+
// → POST /api/v1/invoices/:id/approve
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Standalone** — custom endpoint not tied to a record:
|
|
424
|
+
```typescript
|
|
425
|
+
chat: {
|
|
426
|
+
standalone: true,
|
|
427
|
+
path: "/chat",
|
|
428
|
+
method: "POST",
|
|
429
|
+
responseType: "stream",
|
|
430
|
+
input: z.object({ message: z.string() }),
|
|
431
|
+
guard: { roles: ["member"] },
|
|
432
|
+
handler: "./handlers/chat",
|
|
433
|
+
}
|
|
434
|
+
// → POST /api/v1/todos/chat
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Action Options
|
|
438
|
+
|
|
439
|
+
| Option | Values | Description |
|
|
440
|
+
|--------|--------|-------------|
|
|
441
|
+
| `standalone` | `true` | Not tied to a record |
|
|
442
|
+
| `method` | `GET`, `POST`, `PUT`, `PATCH`, `DELETE` | HTTP method (default: POST) |
|
|
443
|
+
| `responseType` | `json`, `stream`, `file` | Response format |
|
|
444
|
+
| `sideEffects` | `sync`, `async`, `fire-and-forget` | Execution hint |
|
|
445
|
+
|
|
252
446
|
---
|
|
253
447
|
|
|
254
448
|
# API Reference
|
|
@@ -264,6 +458,23 @@ export default {
|
|
|
264
458
|
| `DELETE` | `/api/v1/{resource}/:id` | Delete record |
|
|
265
459
|
| `PUT` | `/api/v1/{resource}/:id` | Upsert (if enabled) |
|
|
266
460
|
|
|
461
|
+
## Batch Operations
|
|
462
|
+
|
|
463
|
+
| Method | Endpoint | Description |
|
|
464
|
+
|--------|----------|-------------|
|
|
465
|
+
| `POST` | `/api/v1/{resource}/batch` | Batch create |
|
|
466
|
+
| `PATCH` | `/api/v1/{resource}/batch` | Batch update |
|
|
467
|
+
| `DELETE` | `/api/v1/{resource}/batch` | Batch delete |
|
|
468
|
+
| `PUT` | `/api/v1/{resource}/batch` | Batch upsert (if enabled) |
|
|
469
|
+
|
|
470
|
+
```json
|
|
471
|
+
// Request
|
|
472
|
+
{ "records": [{ "title": "A" }, { "title": "B" }], "options": { "atomic": false } }
|
|
473
|
+
|
|
474
|
+
// Response
|
|
475
|
+
{ "success": [/* ... */], "errors": [/* ... */], "meta": { "total": 2, "succeeded": 2, "failed": 0 } }
|
|
476
|
+
```
|
|
477
|
+
|
|
267
478
|
## Query Parameters
|
|
268
479
|
|
|
269
480
|
### Pagination
|
|
@@ -271,12 +482,13 @@ export default {
|
|
|
271
482
|
| Param | Default | Max |
|
|
272
483
|
|-------|---------|-----|
|
|
273
484
|
| `limit` | 50 | 100 |
|
|
274
|
-
| `offset` | 0 |
|
|
485
|
+
| `offset` | 0 | — |
|
|
275
486
|
|
|
276
487
|
### Filtering Operators
|
|
277
488
|
|
|
278
489
|
| Operator | Example | SQL |
|
|
279
490
|
|----------|---------|-----|
|
|
491
|
+
| (none) | `?status=active` | `status = 'active'` |
|
|
280
492
|
| `.gt` | `?amount.gt=100` | `amount > 100` |
|
|
281
493
|
| `.gte` | `?amount.gte=100` | `amount >= 100` |
|
|
282
494
|
| `.lt` | `?amount.lt=1000` | `amount < 1000` |
|
|
@@ -306,44 +518,6 @@ export default {
|
|
|
306
518
|
|
|
307
519
|
---
|
|
308
520
|
|
|
309
|
-
# Resource Styles
|
|
310
|
-
|
|
311
|
-
## Controlled Resources (Default)
|
|
312
|
-
|
|
313
|
-
Server generates IDs, guards restrict modifications.
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
// HTTP Methods: POST, PATCH, GET, LIST, DELETE
|
|
317
|
-
export default defineTable(invoices, {
|
|
318
|
-
guards: {
|
|
319
|
-
createable: ['description', 'amount'],
|
|
320
|
-
updatable: ['description'],
|
|
321
|
-
},
|
|
322
|
-
crud: { create: {}, update: {}, get: {}, list: {}, delete: {} },
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
## External/Sync Resources (PUT enabled)
|
|
327
|
-
|
|
328
|
-
Client provides IDs, no field restrictions.
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
// Database config: generateId: false
|
|
332
|
-
// HTTP Methods: POST, PUT, PATCH, GET, LIST, DELETE
|
|
333
|
-
export default defineTable(external_orders, {
|
|
334
|
-
guards: false, // Enables PUT
|
|
335
|
-
crud: {
|
|
336
|
-
put: { access: { roles: ['sync-service'] } },
|
|
337
|
-
get: {},
|
|
338
|
-
list: {}
|
|
339
|
-
},
|
|
340
|
-
});
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
**PUT Rule**: Only available when BOTH `generateId: false` AND `guards: false`.
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
|
|
347
521
|
# Webhooks
|
|
348
522
|
|
|
349
523
|
## Configuration
|
|
@@ -353,7 +527,7 @@ export default defineTable(external_orders, {
|
|
|
353
527
|
providers: {
|
|
354
528
|
database: defineDatabase("cloudflare-d1", {
|
|
355
529
|
splitDatabases: true,
|
|
356
|
-
webhooksBinding: "WEBHOOKS_DB",
|
|
530
|
+
webhooksBinding: "WEBHOOKS_DB",
|
|
357
531
|
}),
|
|
358
532
|
}
|
|
359
533
|
```
|
|
@@ -396,13 +570,46 @@ await emitWebhookEvent(
|
|
|
396
570
|
|
|
397
571
|
---
|
|
398
572
|
|
|
573
|
+
# CLI Commands
|
|
574
|
+
|
|
575
|
+
## Core Commands
|
|
576
|
+
|
|
577
|
+
| Command | Description |
|
|
578
|
+
|---------|-------------|
|
|
579
|
+
| `quickback create <template> <name>` | Create project from template |
|
|
580
|
+
| `quickback compile` | Compile definitions to output |
|
|
581
|
+
| `quickback login` | Authenticate for Pro templates |
|
|
582
|
+
| `quickback logout` | Clear stored credentials |
|
|
583
|
+
| `quickback whoami` | Show current auth status |
|
|
584
|
+
|
|
585
|
+
## Available Templates
|
|
586
|
+
|
|
587
|
+
| Template | Stack | Status |
|
|
588
|
+
|----------|-------|--------|
|
|
589
|
+
| `cloudflare` | Cloudflare Workers + D1 + Better Auth | Free |
|
|
590
|
+
| `bun` | Bun + SQLite + Better Auth | Free |
|
|
591
|
+
| `turso` | Turso/LibSQL + Better Auth | Pro |
|
|
592
|
+
|
|
593
|
+
## Claude Code Integration
|
|
594
|
+
|
|
595
|
+
| Command | Description |
|
|
596
|
+
|---------|-------------|
|
|
597
|
+
| `quickback claude install` | Interactive install of Claude Code skill |
|
|
598
|
+
| `quickback claude install --global` | Install globally |
|
|
599
|
+
| `quickback claude install --local` | Install for current project |
|
|
600
|
+
| `quickback claude update` | Update to latest skill version |
|
|
601
|
+
| `quickback claude remove` | Remove the skill |
|
|
602
|
+
| `quickback claude status` | Show install status |
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
399
606
|
# Development Workflow
|
|
400
607
|
|
|
401
608
|
```bash
|
|
402
609
|
# 1. Create project
|
|
403
610
|
quickback create cloudflare my-app
|
|
404
611
|
|
|
405
|
-
# 2. Define features in
|
|
612
|
+
# 2. Define features in quickback/features/
|
|
406
613
|
|
|
407
614
|
# 3. Compile
|
|
408
615
|
quickback compile
|
|
@@ -416,32 +623,43 @@ npm run dev
|
|
|
416
623
|
|
|
417
624
|
---
|
|
418
625
|
|
|
419
|
-
#
|
|
420
|
-
|
|
421
|
-
| Command | Description |
|
|
422
|
-
|---------|-------------|
|
|
423
|
-
| `quickback create <template> <name>` | Create project from template |
|
|
424
|
-
| `quickback compile` | Compile definitions to output |
|
|
425
|
-
| `quickback login` | Authenticate for Pro templates |
|
|
426
|
-
| `quickback logout` | Clear stored credentials |
|
|
427
|
-
| `quickback whoami` | Show current auth status |
|
|
626
|
+
# Deployment Targets
|
|
428
627
|
|
|
429
|
-
|
|
628
|
+
## Cloudflare Stack (Full Edge)
|
|
430
629
|
|
|
431
|
-
|
|
630
|
+
Hono API on Workers, D1 database, Better Auth, R2 file storage, KV, Queues, Durable Objects for realtime.
|
|
432
631
|
|
|
632
|
+
```typescript
|
|
633
|
+
defineConfig({
|
|
634
|
+
providers: {
|
|
635
|
+
runtime: defineRuntime("cloudflare"),
|
|
636
|
+
database: defineDatabase("cloudflare-d1"),
|
|
637
|
+
auth: defineAuth("better-auth"),
|
|
638
|
+
},
|
|
639
|
+
});
|
|
433
640
|
```
|
|
434
|
-
|
|
641
|
+
|
|
642
|
+
## Supabase (RLS Policies)
|
|
643
|
+
|
|
644
|
+
Compiles Quickback definitions into Row Level Security policies for Postgres-level security.
|
|
645
|
+
|
|
646
|
+
```typescript
|
|
647
|
+
defineConfig({
|
|
648
|
+
providers: {
|
|
649
|
+
runtime: defineRuntime("supabase"),
|
|
650
|
+
database: defineDatabase("supabase"),
|
|
651
|
+
auth: defineAuth("supabase"),
|
|
652
|
+
},
|
|
653
|
+
});
|
|
435
654
|
```
|
|
436
655
|
|
|
437
|
-
|
|
438
|
-
2. **Access**: Checks role + record permissions (403 on failure)
|
|
439
|
-
3. **Access**: Checks role + record permissions (403 on failure)
|
|
440
|
-
4. **Guards**: Validates field modifications (400 on failure)
|
|
441
|
-
5. **Database**: Executes query
|
|
442
|
-
6. **Masking**: Transforms sensitive fields in response
|
|
656
|
+
## Neon (Serverless Postgres)
|
|
443
657
|
|
|
444
|
-
|
|
658
|
+
Serverless Postgres with compiled RLS policies and branching support.
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
# Error Responses
|
|
445
663
|
|
|
446
664
|
| Status | Layer | Meaning |
|
|
447
665
|
|--------|-------|---------|
|
|
@@ -450,3 +668,35 @@ Request → Firewall → Access → Guards → Database → Masking → Response
|
|
|
450
668
|
| `404` | Firewall | Record not found or outside scope |
|
|
451
669
|
| `400` | Guards | Invalid field modification |
|
|
452
670
|
| `429` | Rate limit | Too many requests |
|
|
671
|
+
|
|
672
|
+
---
|
|
673
|
+
|
|
674
|
+
# Database Dialects
|
|
675
|
+
|
|
676
|
+
Detect from `quickback.config.ts` and use the correct imports:
|
|
677
|
+
|
|
678
|
+
| Stack | Import | Table Function |
|
|
679
|
+
|-------|--------|----------------|
|
|
680
|
+
| Cloudflare D1 / SQLite | `drizzle-orm/sqlite-core` | `sqliteTable` |
|
|
681
|
+
| Supabase / PostgreSQL | `drizzle-orm/pg-core` | `pgTable` |
|
|
682
|
+
| MySQL | `drizzle-orm/mysql-core` | `mysqlTable` |
|
|
683
|
+
|
|
684
|
+
---
|
|
685
|
+
|
|
686
|
+
# Documentation
|
|
687
|
+
|
|
688
|
+
Full documentation at https://docs.quickback.dev
|
|
689
|
+
|
|
690
|
+
- [Quick Start](https://docs.quickback.dev/definitions/quick-start)
|
|
691
|
+
- [Concepts](https://docs.quickback.dev/definitions/concepts)
|
|
692
|
+
- [Database Schema](https://docs.quickback.dev/definitions/database-schema)
|
|
693
|
+
- [CRUD Endpoints](https://docs.quickback.dev/definitions/crud-endpoints)
|
|
694
|
+
- [Views](https://docs.quickback.dev/definitions/views)
|
|
695
|
+
- [Actions](https://docs.quickback.dev/definitions/actions)
|
|
696
|
+
- [Firewall](https://docs.quickback.dev/definitions/firewall)
|
|
697
|
+
- [Access](https://docs.quickback.dev/definitions/access)
|
|
698
|
+
- [Guards](https://docs.quickback.dev/definitions/guards)
|
|
699
|
+
- [Masking](https://docs.quickback.dev/definitions/masking)
|
|
700
|
+
- [CLI Reference](https://docs.quickback.dev/compiler/cli)
|
|
701
|
+
- [Cloudflare Stack](https://docs.quickback.dev/stack/cloudflare)
|
|
702
|
+
- [Quick Reference](https://docs.quickback.dev/stack/reference)
|