# Getting Started

Get started with Quickback in minutes. This guide shows you how to define a complete table with security configuration.

## Start a Project

```bash
mkdir my-app && cd my-app
npx @quickback-dev/cli start
```

`quickback 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?

```bash
npm install -g @quickback-dev/cli
quickback create cloudflare my-app
cd my-app
```

Both paths scaffold the same project:
- `quickback.config.ts` — Project configuration
- `quickback/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.ts
```

## Complete Example

Here's a complete `jobs` table with all security layers in a single `feature()` call:

```typescript
// 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:

```typescript
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](/compiler/definitions/feature) and [Schema: choosing between `q` and Drizzle](/compiler/definitions/schema#choosing-between-q-and-drizzle) for the full picture.

## What Each Layer Does

1. **Firewall**: Automatically adds `WHERE organizationId = ?` to every query. Users in Org A can never see Org B's data.
2. **Guards**: Controls which fields can be modified — `createable` for POST, `updatable` for PATCH, `protected` for action-only fields.
3. **Write Access**: Role-based access control for each operation. All roles can read, only hiring managers can write.

## Compile and Run

```bash
# 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 dev
```

The 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:

```bash
# 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](/compiler/getting-started/templates) — Detailed setup guides
- [Full Example](/compiler/getting-started/full-example) — Complete resource walkthrough
- [Database Schema](/compiler/definitions/schema) — Column types, relations, audit fields
- [Firewall](/compiler/definitions/firewall) — Data isolation patterns
- [Access](/compiler/definitions/access) — Role & condition-based control
- [Guards](/compiler/definitions/guards) — Field modification rules
- [Masking](/compiler/definitions/masking) — Field redaction for sensitive data
- [Actions](/compiler/definitions/actions) — Custom business logic endpoints

## See Also

- [Quickback Stack](/stack) — The runtime environment where your compiled API runs (D1, KV, R2, auth)
- [Account UI](/stack/account-ui) — Pre-built authentication and account management UI
- [Using the API](/compiler/targets/hono-api/using-the-api) — CRUD endpoints, filtering, and batch operations
