Getting Started
Get started with Quickback in minutes. Learn how to define database tables with security configuration and compile them into a production-ready API.
Get started with Quickback in minutes. This guide shows you how to define a complete table with security configuration.
Start a Project
mkdir my-app && cd my-app
npx @quickback-dev/cli startquickback start is interactive — it asks for a template and project name, scaffolds the project on disk, and only prompts for an account just before compiling. If you cancel at the login prompt the scaffold stays put; finish later with quickback login && quickback compile.
Prefer a scriptable form?
npm install -g @quickback-dev/cli
quickback create cloudflare my-app
cd my-appBoth paths scaffold the same project:
quickback.config.ts— Project configurationquickback/features/— Your table definitions- Working example feature with full security configuration (when you pick
todos/blog/saas)
Available templates:
cloudflare— Cloudflare Workers + D1 + Better Auth, bare scaffold (free)todos— Working CRUD example with masking + actions (free)blog— Single-tenant blog, PUBLIC reads, admin writes (free)empty— Cloudflare scaffold, no example features (free)saas— Full B2B SaaS with orgs, R2 file storage, webhooks (pro)
File Structure
Each table gets its own file with schema and security config together:
quickback/
├── quickback.config.ts
└── features/
└── jobs/
├── jobs.ts # Table + security config
├── applications.ts # Related table + config
├── actions.ts # Custom actions (optional)
└── handlers/ # Action handlers (optional)
└── close-job.tsComplete Example
Here's a complete jobs table with all security layers in a single feature() call:
// quickback/features/jobs/jobs.ts
import { feature, q } from '@quickback/compiler';
export default feature('jobs', {
columns: {
id: q.id(),
title: q.text().required(),
department: q.text().required(),
status: q.text().default('draft').required(), // draft | open | closed
salaryMin: q.int().optional(),
salaryMax: q.int().optional(),
// Ownership — required for firewall data isolation
organizationId: q.scope("organization"),
},
// 1. FIREWALL — Data isolation
firewall: [{ field: 'organizationId', equals: 'ctx.activeOrgId' }],
// 2. GUARDS — Field modification rules
guards: {
createable: ["title", "department", "status", "salaryMin", "salaryMax"],
updatable: ["title", "department", "status"],
},
// 3. READ — Collection + per-id GET (gates GET / and GET /:id)
read: {
access: { roles: ["owner", "hiring-manager", "recruiter", "interviewer"] },
pageSize: 25,
},
// 4. WRITE OPERATIONS
create: { access: { roles: ["owner", "hiring-manager"] } },
update: { access: { roles: ["owner", "hiring-manager"] } },
delete: { access: { roles: ["owner", "hiring-manager"] }, mode: "soft" },
});One import, one export, every security layer declared in one place. Secure by default — no write routes ship until you opt in with top-level create, update, delete, or upsert.
Alternatives — same result, different shape
If you'd rather split the table declaration from the security config (e.g. to reference the table in an action file), use the two-export form. Identical output:
import { q, defineTable } from '@quickback/compiler';
export const jobs = q.table('jobs', {
id: q.id(),
title: q.text().required(),
// … same columns
});
export default defineTable(jobs, {
firewall: [{ field: 'organizationId', equals: 'ctx.activeOrgId' }],
// … same config
});
export type Job = typeof jobs.$infer;Or if you're interop'ing with existing Drizzle schemas, you can author with sqliteTable / pgTable directly — the compiler dispatches per file, and all forms can coexist in the same project.
See feature() — single-export sugar and Schema: choosing between q and Drizzle for the full picture.
What Each Layer Does
- Firewall: Automatically adds
WHERE organizationId = ?to every query. Users in Org A can never see Org B's data. - Guards: Controls which fields can be modified —
createablefor POST,updatablefor PATCH,protectedfor action-only fields. - Write Access: Role-based access control for each operation. All roles can read, only hiring managers can write.
Compile and Run
# Compile your definitions — prompts you to sign in or sign up
# if you haven't already (no separate `quickback login` step needed)
quickback compile
# Run locally
npm run devThe first compile prompts for an account; subsequent compiles re-use the stored session.
Generated Endpoints
Quickback generates these endpoints from the example above:
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/jobs | List jobs (all roles) |
GET | /api/v1/jobs/:id | Get single job |
POST | /api/v1/jobs | Create job (hiring managers only) |
PATCH | /api/v1/jobs/:id | Update job (hiring managers only) |
DELETE | /api/v1/jobs/:id | Soft delete job (hiring managers only) |
Test Your API
After quickback compile and npm run dev, your API is running locally. Open a second terminal and try these requests:
# 1. Create a user account
curl -X POST http://localhost:8787/api/auth/sign-up/email \
-H "Content-Type: application/json" \
-d '{"email": "admin@example.com", "password": "securepassword123", "name": "Admin"}'
# 2. Sign in and get a session token
curl -X POST http://localhost:8787/api/auth/sign-in/email \
-H "Content-Type: application/json" \
-d '{"email": "admin@example.com", "password": "securepassword123"}'
# → Response includes a session token in Set-Cookie header
# 3. Create a record (use the session cookie from step 2)
curl -X POST http://localhost:8787/api/v1/jobs \
-H "Content-Type: application/json" \
-H "Cookie: better-auth.session_token=<token>" \
-d '{"title": "Senior Engineer", "department": "Engineering", "status": "open"}'
# 4. List records
curl http://localhost:8787/api/v1/jobs \
-H "Cookie: better-auth.session_token=<token>"Cloudflare templates run on port 8787 (wrangler dev). Check your terminal output for the exact URL.
Next Steps
- Template Walkthroughs — Detailed setup guides
- Full Example — Complete resource walkthrough
- Database Schema — Column types, relations, audit fields
- Firewall — Data isolation patterns
- Access — Role & condition-based control
- Guards — Field modification rules
- Masking — Field redaction for sensitive data
- Actions — Custom business logic endpoints
See Also
- Quickback Stack — The runtime environment where your compiled API runs (D1, KV, R2, auth)
- Account UI — Pre-built authentication and account management UI
- Using the API — CRUD endpoints, filtering, and batch operations