@kardoe/quickback 0.6.0 → 0.6.1
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/create.js +24 -27
- package/dist/commands/create.js.map +1 -1
- package/dist/cursor/quickback.mdc +33 -0
- package/dist/docs/content.js +17 -17
- package/dist/docs/content.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +3 -1
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/file-loader.d.ts.map +1 -1
- package/dist/lib/file-loader.js +11 -0
- package/dist/lib/file-loader.js.map +1 -1
- package/dist/skill/skill/SKILL.md +28 -1
- package/dist/skill/skill/agents/quickback-specialist/AGENT.md +33 -0
- package/package.json +1 -1
- package/src/cursor/quickback.mdc +33 -0
- package/src/skill/SKILL.md +28 -1
- package/src/skill/agents/quickback-specialist/AGENT.md +33 -0
package/dist/docs/content.js
CHANGED
|
@@ -7,7 +7,7 @@ export const DOCS = {
|
|
|
7
7
|
},
|
|
8
8
|
"account-ui/environment-variables": {
|
|
9
9
|
"title": "Environment Variables",
|
|
10
|
-
"content": "# Environment Variables\n\nConfigure the Account UI by setting environment variables in your `.env` file or deployment platform.\n\n## Required Variables\n\nThese variables are **required** for the Account UI to function:\n\n### API Configuration\n\n```bash\n# Quickback API URL\nVITE_API_URL=https://api.example.com\n\n# Account UI URL (where the Account UI is deployed)\nVITE_ACCOUNT_APP_URL=https://account.example.com\n```\n\n## App Identity\n\nConfigure your application's basic information:\n\n```bash\n# App name (displayed in UI)\nVITE_APP_NAME=My Application\n\n# Tagline (shown on landing pages)\nVITE_APP_TAGLINE=Build faster, ship sooner\n\n# Full description\nVITE_APP_DESCRIPTION=A complete platform for building modern web applications\n\n# Company information\nVITE_COMPANY_NAME=My Company Inc.\nVITE_COMPANY_ADDRESS=123 Main St, Suite 100, San Francisco, CA 94105\n```\n\n## URLs\n\n### Main Application\n\n```bash\n# Your main application URL\n# Users will be redirected here after authentication\nVITE_APP_URL=https://app.example.com\n\n# Organization/Tenant URL pattern (optional)\n# Use {slug} as placeholder for organization identifier\n# Examples:\n# /organizations/{slug} -> app.example.com/organizations/acme\n# /workspace/{slug} -> app.example.com/workspace/acme\n# https://{slug}.example.com -> acme.example.com\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\n```\n\n### Support and Legal\n\n```bash\n# Support/help URL\nVITE_SUPPORT_URL=https://support.example.com\n\n# Privacy policy URL\nVITE_PRIVACY_URL=https://example.com/privacy\n\n# Terms of service URL\nVITE_TERMS_URL=https://example.com/terms\n```\n\n## Email Configuration\n\n```bash\n# From address for all emails\nVITE_EMAIL_FROM=noreply@example.com\n\n# Reply-to address\nVITE_EMAIL_REPLY_TO=support@example.com\n\n# Support email address\nVITE_SUPPORT_EMAIL=support@example.com\n\n# AWS Region for SES (if using AWS SES)\nVITE_EMAIL_REGION=us-east-1\n```\n\n## Branding\n\n```bash\n# Primary theme color (hex code)\nVITE_THEME_COLOR=#1e293b\n\n# SEO keywords (comma-separated)\nVITE_SEO_KEYWORDS=saas,authentication,account management\n```\n\n## Feature Flags\n\nEnable or disable features using boolean environment variables:\n\n### Authentication Features\n\n```bash\n# Enable user signup (default: true)\nENABLE_SIGNUP=true\n\n# Enable email verification (default: true)\nENABLE_EMAIL_VERIFICATION=true\n\n# Disable email deliverability checks (default: false)\n# Set to true to skip checking if email addresses are deliverable\nDISABLE_EMAIL_STATUS_CHECK=false\n\n# Enable passkey (WebAuthn)
|
|
10
|
+
"content": "# Environment Variables\n\nConfigure the Account UI by setting environment variables in your `.env` file or deployment platform.\n\n## Required Variables\n\nThese variables are **required** for the Account UI to function:\n\n### API Configuration\n\n```bash\n# Quickback API URL\nVITE_API_URL=https://api.example.com\n\n# Account UI URL (where the Account UI is deployed)\nVITE_ACCOUNT_APP_URL=https://account.example.com\n```\n\n## App Identity\n\nConfigure your application's basic information:\n\n```bash\n# App name (displayed in UI)\nVITE_APP_NAME=My Application\n\n# Tagline (shown on landing pages)\nVITE_APP_TAGLINE=Build faster, ship sooner\n\n# Full description\nVITE_APP_DESCRIPTION=A complete platform for building modern web applications\n\n# Company information\nVITE_COMPANY_NAME=My Company Inc.\nVITE_COMPANY_ADDRESS=123 Main St, Suite 100, San Francisco, CA 94105\n```\n\n## URLs\n\n### Main Application\n\n```bash\n# Your main application URL\n# Users will be redirected here after authentication\nVITE_APP_URL=https://app.example.com\n\n# Organization/Tenant URL pattern (optional)\n# Use {slug} as placeholder for organization identifier\n# Examples:\n# /organizations/{slug} -> app.example.com/organizations/acme\n# /workspace/{slug} -> app.example.com/workspace/acme\n# https://{slug}.example.com -> acme.example.com\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\n```\n\n### Support and Legal\n\n```bash\n# Support/help URL\nVITE_SUPPORT_URL=https://support.example.com\n\n# Privacy policy URL\nVITE_PRIVACY_URL=https://example.com/privacy\n\n# Terms of service URL\nVITE_TERMS_URL=https://example.com/terms\n```\n\n## Email Configuration\n\n```bash\n# From address for all emails\nVITE_EMAIL_FROM=noreply@example.com\n\n# Reply-to address\nVITE_EMAIL_REPLY_TO=support@example.com\n\n# Support email address\nVITE_SUPPORT_EMAIL=support@example.com\n\n# AWS Region for SES (if using AWS SES)\nVITE_EMAIL_REGION=us-east-1\n```\n\n## Branding\n\n```bash\n# Primary theme color (hex code)\nVITE_THEME_COLOR=#1e293b\n\n# SEO keywords (comma-separated)\nVITE_SEO_KEYWORDS=saas,authentication,account management\n```\n\n## Feature Flags\n\nEnable or disable features using boolean environment variables:\n\n### Authentication Features\n\n```bash\n# Enable user signup (default: true)\nENABLE_SIGNUP=true\n\n# Enable email verification after signup (default: true)\nENABLE_EMAIL_VERIFICATION=true\n\n# Disable email deliverability checks (default: false)\n# Set to true to skip checking if email addresses are deliverable\nDISABLE_EMAIL_STATUS_CHECK=false\n\n# Enable passkey (WebAuthn) login (default: true)\nENABLE_PASSKEY=true\n\n# Enable email OTP login and signup (default: true)\nENABLE_EMAIL_OTP=true\n\n# Enable email+password login and signup (default: false)\nENABLE_PASSWORD=false\n```\n\n### Account Management Features\n\n```bash\n# Enable file uploads (avatar, etc.) (default: false)\nVITE_ENABLE_FILE_UPLOADS=false\n```\n\n### Organization Features\n\n```bash\n# Enable multi-tenant organizations (default: true)\nENABLE_ORGANIZATIONS=true\n```\n\n### Admin Features\n\n```bash\n# Enable admin panel (default: true)\nENABLE_ADMIN=true\n```\n\n## Complete Example\n\nHere's a complete `.env` file with all common configuration:\n\n```bash title=\".env\"\n# ===== REQUIRED =====\nVITE_API_URL=https://api.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\n\n# ===== APP IDENTITY =====\nVITE_APP_NAME=Acme SaaS\nVITE_APP_TAGLINE=Build faster, ship sooner\nVITE_APP_DESCRIPTION=The complete platform for modern web applications\nVITE_COMPANY_NAME=Acme Corporation\nVITE_COMPANY_ADDRESS=123 Main St, Suite 100, San Francisco, CA 94105\n\n# ===== URLS =====\nVITE_APP_URL=https://app.acme.com\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\nVITE_SUPPORT_URL=https://help.acme.com\nVITE_PRIVACY_URL=https://acme.com/privacy\nVITE_TERMS_URL=https://acme.com/terms\n\n# ===== EMAIL =====\nVITE_EMAIL_FROM=noreply@acme.com\nVITE_EMAIL_REPLY_TO=support@acme.com\nVITE_SUPPORT_EMAIL=support@acme.com\nVITE_EMAIL_REGION=us-east-1\n\n# ===== BRANDING =====\nVITE_THEME_COLOR=#3b82f6\n\n# ===== SEO =====\nVITE_SEO_KEYWORDS=saas,project management,team collaboration\n\n# ===== FEATURES =====\nENABLE_SIGNUP=true\nENABLE_EMAIL_VERIFICATION=true\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\nENABLE_PASSWORD=false\nENABLE_ORGANIZATIONS=true\nENABLE_ADMIN=true\nVITE_ENABLE_FILE_UPLOADS=true\nDISABLE_EMAIL_STATUS_CHECK=false\n```\n\n## Environment-Specific Configuration\n\n### Development\n\n```bash title=\".env.development\"\nVITE_API_URL=http://localhost:8787\nVITE_ACCOUNT_APP_URL=http://localhost:5173\nVITE_APP_URL=http://localhost:3000\nDISABLE_EMAIL_STATUS_CHECK=true\n```\n\n### Staging\n\n```bash title=\".env.staging\"\nVITE_API_URL=https://api-staging.example.com\nVITE_ACCOUNT_APP_URL=https://account-staging.example.com\nVITE_APP_URL=https://app-staging.example.com\n```\n\n### Production\n\n```bash title=\".env.production\"\nVITE_API_URL=https://api.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\nVITE_APP_URL=https://app.example.com\nDISABLE_EMAIL_STATUS_CHECK=false\n```\n\n## Cloudflare Pages Setup\n\nWhen deploying to Cloudflare Pages, set environment variables in the dashboard:\n\n1. Go to **Settings** → **Environment Variables**\n2. Add each `VITE_*` variable\n3. Separate values for **Production** and **Preview** branches\n\n## Wrangler Configuration\n\nFor `wrangler dev` and `wrangler deploy`, add variables to `wrangler.toml`:\n\n```toml title=\"wrangler.toml\"\n[env.production.vars]\nVITE_API_URL = \"https://api.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account.example.com\"\nVITE_APP_NAME = \"My App\"\n# ... other variables\n```\n\nOr use `.dev.vars` for local development (git-ignored):\n\n```bash title=\".dev.vars\"\nVITE_API_URL=http://localhost:8787\nVITE_ACCOUNT_APP_URL=http://localhost:5173\n```\n\n## Runtime Configuration\n\nSome values can be overridden at runtime using the config API:\n\n```ts\n\nsetAppConfig({\n name: 'My Custom App',\n branding: {\n primaryColor: '#ff6600',\n logoUrl: '/custom-logo.png',\n },\n urls: {\n base: 'https://account.custom.com',\n app: 'https://app.custom.com',\n },\n});\n```\n\n## Validation\n\nThe Account UI validates configuration on startup:\n\n- **Missing VITE_API_URL**: Throws error\n- **Invalid URLs**: Logs warning\n- **Missing optional fields**: Uses defaults\n\nCheck the browser console for configuration warnings.\n\n## Next Steps\n\n- **[Feature Flags](/account-ui/features)** - Detailed feature configuration\n- **[Customization](/account-ui/customization)** - Customize labels and messages\n- **[Worker Setup](/account-ui/worker)** - Deploy to Cloudflare"
|
|
11
11
|
},
|
|
12
12
|
"account-ui/features/admin": {
|
|
13
13
|
"title": "Admin Panel",
|
|
@@ -27,7 +27,7 @@ export const DOCS = {
|
|
|
27
27
|
},
|
|
28
28
|
"account-ui/features": {
|
|
29
29
|
"title": "Feature Flags",
|
|
30
|
-
"content": "# Feature Flags\n\nControl which features are available in your Account UI deployment using feature flags. All features are configured via environment variables.\n\n## Authentication Features\n\n### User Signup\n\n```bash\nENABLE_SIGNUP=true # default: true\n```\n\n**When enabled:**\n- Shows \"Sign Up\" link on login page\n- `/signup` route is accessible\n- New users can create accounts\n\n**When disabled:**\n- Signup route returns 404\n- Only existing users can log in\n- Useful for invite-only applications\n\n### Email Verification\n\n```bash\nENABLE_EMAIL_VERIFICATION=true # default: true\n```\n\n**When enabled:**\n- Users must verify email before full access\n- Verification email sent on signup\n- \"Resend verification email\" option available\n- Unverified users see verification prompt\n\n**When disabled:**\n- Email addresses are trusted without verification\n- Users have immediate access after signup\n\n### Email Deliverability Check\n\n```bash\nDISABLE_EMAIL_STATUS_CHECK=false # default: false\n```\n\n**When `false` (checking enabled):**\n- System validates email addresses are deliverable\n- Rejects disposable/temporary email providers\n- Prevents typos in domain names\n\n**When `true` (checking disabled):**\n- Accepts all email formats\n- Useful for development/testing\n- Allows `@test.com`, `@localhost`, etc.\n\n###
|
|
30
|
+
"content": "# Feature Flags\n\nControl which features are available in your Account UI deployment using feature flags. All features are configured via environment variables.\n\n## Authentication Features\n\n### User Signup\n\n```bash\nENABLE_SIGNUP=true # default: true\n```\n\nControls whether new users can register. Wired to the `ENABLE_SIGNUP` env var.\n\n**When enabled:**\n- Shows \"Sign Up\" link on login page\n- `/signup` route is accessible\n- New users can create accounts\n\n**When disabled:**\n- Signup route returns 404\n- Only existing users can log in\n- Useful for invite-only applications\n\n### Email Verification\n\n```bash\nENABLE_EMAIL_VERIFICATION=true # default: true\n```\n\nControls the email verification flow. Wired to the frontend via the `ENABLE_EMAIL_VERIFICATION` env var.\n\n**When enabled:**\n- Users must verify email before full access\n- Verification email sent on signup\n- \"Resend verification email\" option available\n- Unverified users see verification prompt\n\n**When disabled:**\n- Email addresses are trusted without verification\n- Users have immediate access after signup\n\n### Email Deliverability Check\n\n```bash\nDISABLE_EMAIL_STATUS_CHECK=false # default: false\n```\n\n**When `false` (checking enabled):**\n- System validates email addresses are deliverable\n- Rejects disposable/temporary email providers\n- Prevents typos in domain names\n\n**When `true` (checking disabled):**\n- Accepts all email formats\n- Useful for development/testing\n- Allows `@test.com`, `@localhost`, etc.\n\n### Passkey Login\n\n```bash\nENABLE_PASSKEY=true # default: true\n```\n\nControls passkey LOGIN only (not signup). When enabled, users can authenticate using passkeys.\n\n**When enabled:**\n- Users can register passkeys (fingerprint, Face ID, hardware keys)\n- Passwordless login option\n- \"Manage Passkeys\" page available\n\n**When disabled:**\n- No passkey registration\n- No passkey login option\n\n**Requirements:**\n- HTTPS (passkeys require secure context)\n- Modern browser with WebAuthn support\n\n### Email OTP\n\n```bash\nENABLE_EMAIL_OTP=true # default: true\n```\n\n**When enabled:**\n- Users can receive one-time passwords via email\n- Alternative to password login\n- `/email-otp` route available\n\n**When disabled:**\n- No email OTP option\n- Password or passkey required\n\n### Password Authentication\n\n```bash\nENABLE_PASSWORD=false # default: false\n```\n\n**When enabled:**\n- Email + password fields on login page\n- Password field on signup page\n- Traditional username/password authentication\n\n**When disabled:**\n- No password fields shown\n- Users authenticate via email OTP or passkey\n\n## Account Management Features\n\n### File Uploads\n\n```bash\nVITE_ENABLE_FILE_UPLOADS=false # default: false\n```\n\n**When enabled:**\n- Avatar/profile picture upload\n- Image cropping and editing\n- File upload to R2/S3\n\n**When disabled:**\n- No file upload functionality\n- Users can only use default avatars\n\n**Requirements:**\n- R2 bucket or S3 configured\n- Upload endpoints in your API\n\n## Organization Features\n\n### Organizations (Multi-Tenancy)\n\n```bash\nENABLE_ORGANIZATIONS=true # default: true\n```\n\n**When enabled:**\n- Users can create organizations\n- Organization management pages\n- Member invitations and roles\n- `/organizations/*` routes\n\n**When disabled:**\n- Single-user mode only\n- No organization features\n- Simpler user experience\n\n**Includes:**\n- Organization creation and deletion\n- Member management (owner, admin, member roles)\n- Invitation system\n- Organization settings\n\n## Admin Features\n\n### Admin Panel\n\n```bash\nENABLE_ADMIN=true # default: true\n```\n\n**When enabled:**\n- `/admin` route accessible to admin users\n- User management dashboard\n- Subscription management\n- Admin-only features:\n - Create users manually\n - Ban/unban users\n - Reset user passwords\n - View all sessions\n - Manage subscriptions\n\n**When disabled:**\n- No admin panel\n- Admin must use database directly\n\n**Requirements:**\n- User must have admin role in database\n\n## Feature Combinations\n\n### Minimal Configuration (Password-Only)\n\n```bash\nENABLE_PASSKEY=false\nENABLE_EMAIL_OTP=false\nENABLE_PASSWORD=true\nENABLE_ORGANIZATIONS=false\nENABLE_ADMIN=false\n```\n\nSimple email/password authentication for single-tenant apps.\n\n### Maximum Security\n\n```bash\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\nENABLE_PASSWORD=false\nENABLE_EMAIL_VERIFICATION=true\n```\n\nAll authentication methods with email verification and deliverability checks.\n\n### Multi-Tenant SaaS\n\n```bash\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\nENABLE_ORGANIZATIONS=true\nENABLE_ADMIN=true\n```\n\nFull-featured SaaS with organizations, teams, and admin panel.\n\n### Invite-Only Platform\n\n```bash\nENABLE_SIGNUP=false\nENABLE_PASSKEY=true\nENABLE_ORGANIZATIONS=true\nENABLE_ADMIN=true\n```\n\nNo public signup - users must be created by admin or invited to organizations.\n\n## Feature Detection\n\nCheck if a feature is enabled in your code:\n\n```ts\n\nif (isFeatureEnabled('organizations')) {\n // Show organizations menu\n}\n\nif (isFeatureEnabled('passkey')) {\n // Offer passkey setup\n}\n```\n\n## Dynamic Feature Configuration\n\nOverride features at runtime:\n\n```ts\n\nsetAppConfig({\n features: {\n organizations: false, // Disable organizations\n passkey: true, // Enable passkey\n },\n});\n```\n\n## Testing Features\n\nFor local development, create `.env.local`:\n\n```bash title=\".env.local\"\n# Test with all features enabled\nENABLE_SIGNUP=true\nENABLE_EMAIL_VERIFICATION=true\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\nENABLE_PASSWORD=true\nENABLE_ORGANIZATIONS=true\nENABLE_ADMIN=true\nVITE_ENABLE_FILE_UPLOADS=true\nDISABLE_EMAIL_STATUS_CHECK=true # Allow test emails\n```\n\n## Next Steps\n\n- **[Environment Variables](/account-ui/environment-variables)** - Complete variable reference\n- **[Customization](/account-ui/customization)** - Customize UI text and labels\n- **[Worker Setup](/account-ui/worker)** - Deploy your configuration"
|
|
31
31
|
},
|
|
32
32
|
"account-ui/features/organizations": {
|
|
33
33
|
"title": "Organizations",
|
|
@@ -35,7 +35,7 @@ export const DOCS = {
|
|
|
35
35
|
},
|
|
36
36
|
"account-ui/features/passkeys": {
|
|
37
37
|
"title": "Passkeys",
|
|
38
|
-
"content": "When `
|
|
38
|
+
"content": "When `ENABLE_PASSKEY=true`, Account UI provides passkey registration and management.\n\n## Passkey Registration\n\n- Setup wizard guides users through passkey creation\n- Supports fingerprint, Face ID, and hardware security keys\n- Multiple passkeys can be registered per account\n\n## Passkey Login\n\n- \"Sign in with passkey\" button on login page\n- Browser's native WebAuthn dialog\n- No password required\n\n## Managing Passkeys\n\n- View registered passkeys\n- Remove individual passkeys\n- Register additional passkeys\n\n## Requirements\n\n- HTTPS (WebAuthn requires secure context)\n- Modern browser with WebAuthn support\n\n## Related\n\n- [Auth Plugins](/stack/auth/plugins) — Plugin configuration\n- [Environment Variables](/account-ui/environment-variables)"
|
|
39
39
|
},
|
|
40
40
|
"account-ui/features/passwordless": {
|
|
41
41
|
"title": "Passwordless Authentication",
|
|
@@ -55,11 +55,11 @@ export const DOCS = {
|
|
|
55
55
|
},
|
|
56
56
|
"account-ui/standalone": {
|
|
57
57
|
"title": "Standalone Usage",
|
|
58
|
-
"content": "Account UI can be used with **any Better Auth-powered backend**, not just Quickback-compiled projects. This makes it a drop-in authentication frontend for any app using Better Auth.\n\n## Requirements\n\n- A backend running [Better Auth](https://better-auth.com) with API endpoints\n- Node.js 18+ and npm/pnpm/bun\n- A Cloudflare account (for deployment)\n\n## Setup\n\n### 1. Clone or Install the Account UI\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n### 2. Configure the Auth Route Mode\n\nThe key difference from Quickback mode is the `VITE_AUTH_ROUTE` variable. Set it to `better-auth` to use Better Auth's default API paths:\n\n```bash title=\".env\"\n# Auth route mode — use 'better-auth' for standalone\nVITE_AUTH_ROUTE=better-auth\n\n# Your Better Auth backend URL\nVITE_API_URL=https://api.example.com\n\n# Where this Account UI is hosted\nVITE_ACCOUNT_APP_URL=https://account.example.com\n```\n\nThis configures the auth client to use `/api/auth` as the base path (Better Auth's default), instead of Quickback's `/auth/v1`.\n\n### 3. Configure Your App\n\n```bash title=\".env\"\n# App identity\nVITE_APP_NAME=My App\nVITE_APP_TAGLINE=Welcome to My App\n\n# Redirect after login\nVITE_APP_URL=https://app.example.com\n\n# Feature flags\nENABLE_SIGNUP=true\nENABLE_ORGANIZATIONS=true\
|
|
58
|
+
"content": "Account UI can be used with **any Better Auth-powered backend**, not just Quickback-compiled projects. This makes it a drop-in authentication frontend for any app using Better Auth.\n\n## Requirements\n\n- A backend running [Better Auth](https://better-auth.com) with API endpoints\n- Node.js 18+ and npm/pnpm/bun\n- A Cloudflare account (for deployment)\n\n## Setup\n\n### 1. Clone or Install the Account UI\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n### 2. Configure the Auth Route Mode\n\nThe key difference from Quickback mode is the `VITE_AUTH_ROUTE` variable. Set it to `better-auth` to use Better Auth's default API paths:\n\n```bash title=\".env\"\n# Auth route mode — use 'better-auth' for standalone\nVITE_AUTH_ROUTE=better-auth\n\n# Your Better Auth backend URL\nVITE_API_URL=https://api.example.com\n\n# Where this Account UI is hosted\nVITE_ACCOUNT_APP_URL=https://account.example.com\n```\n\nThis configures the auth client to use `/api/auth` as the base path (Better Auth's default), instead of Quickback's `/auth/v1`.\n\n### 3. Configure Your App\n\n```bash title=\".env\"\n# App identity\nVITE_APP_NAME=My App\nVITE_APP_TAGLINE=Welcome to My App\n\n# Redirect after login\nVITE_APP_URL=https://app.example.com\n\n# Feature flags\nENABLE_SIGNUP=true\nENABLE_ORGANIZATIONS=true\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\n```\n\nSee [Environment Variables](/account-ui/environment-variables) for the complete reference.\n\n### 4. Build and Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\n```\n\n## Auth Route Modes\n\nAccount UI supports three routing modes, controlled by `VITE_AUTH_ROUTE`:\n\n| Mode | Value | Auth Path | Data Path | Storage Path |\n|------|-------|-----------|-----------|--------------|\n| Better Auth | `better-auth` | `/api/auth` | — | — |\n| Quickback | `quickback` | `/auth/v1` | `/api/v1` | `/storage/v1` |\n| Custom | `custom` | Custom | Custom | Custom |\n\n### Better Auth Mode (Default)\n\nRoutes all auth requests to `/api/auth/*` — the standard Better Auth convention:\n\n```bash\nVITE_AUTH_ROUTE=better-auth\n```\n\n### Custom Mode\n\nFor non-standard setups, use custom mode with explicit paths:\n\n```bash\nVITE_AUTH_ROUTE=custom\nVITE_AUTH_BASE_PATH=/my-auth\nVITE_DATA_BASE_PATH=/my-api\nVITE_STORAGE_BASE_PATH=/my-storage\n```\n\n## Backend Requirements\n\nYour Better Auth backend must have the following plugins enabled for full Account UI functionality:\n\n| Feature | Required Plugin | Required For |\n|---------|----------------|--------------|\n| Organizations | `organization` | Multi-tenant org management |\n| Admin panel | `admin` | User management dashboard |\n| API keys | `apiKey` | API key generation UI |\n| Passkeys | `@better-auth/passkey` | WebAuthn authentication |\n| Email OTP | `emailOTP` | One-time password login |\n| CLI login | `deviceAuthorization` | `quickback login` flow |\n\nOnly enable the features that match your backend's plugin configuration. Disable features for plugins you haven't installed:\n\n```bash\n# If your backend doesn't have the organization plugin:\nENABLE_ORGANIZATIONS=false\n\n# If you haven't set up passkeys:\nENABLE_PASSKEYS=false\n```\n\n## CORS Configuration\n\nYour Better Auth backend must allow requests from the Account UI's domain. Configure CORS to include:\n\n```typescript\n// In your Better Auth config\ntrustedOrigins: [\n \"https://account.example.com\", // Account UI domain\n],\n```\n\n## Differences from Quickback Mode\n\n| Aspect | Standalone | With Quickback |\n|--------|-----------|----------------|\n| Auth routes | `/api/auth/*` | `/auth/v1/*` |\n| Data API | Not used | `/api/v1/*` |\n| Storage API | Not used | `/storage/v1/*` |\n| File uploads | Manual setup | Auto-configured |\n| Backend | Any Better Auth app | Quickback-compiled |\n\n## Next Steps\n\n- [Environment Variables](/account-ui/environment-variables) — Complete configuration reference\n- [Customization](/account-ui/customization) — Branding, labels, and theming\n- [Worker Setup](/account-ui/worker) — Cloudflare Workers deployment\n- [Feature Flags](/account-ui/features) — Enable and disable features"
|
|
59
59
|
},
|
|
60
60
|
"account-ui/with-quickback": {
|
|
61
61
|
"title": "With Quickback",
|
|
62
|
-
"content": "When using Account UI with a Quickback-compiled backend, set the auth route mode to `quickback` to match Quickback's API structure. This is the recommended setup for Quickback projects.\n\n## Setup\n\nYou can use Account UI with Quickback as either a standalone template or an npm library.\n\n### Option A: Template (Clone the Source)\n\n#### 1. Clone the Template\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n#### 2. Set the Auth Route Mode\n\nConfigure Account UI to use Quickback's route conventions:\n\n```bash title=\".env\"\n# Use Quickback route mode\nVITE_AUTH_ROUTE=quickback\n\n# Your Quickback API URL\nVITE_API_URL=https://api.example.com\n\n# Where this Account UI is hosted\nVITE_ACCOUNT_APP_URL=https://account.example.com\n\n# Your main application URL (redirect after login)\nVITE_APP_URL=https://app.example.com\n```\n\n#### 3. Configure Your App\n\n```bash title=\".env\"\n# App identity\nVITE_APP_NAME=My App\nVITE_APP_TAGLINE=Build faster, ship sooner\nVITE_COMPANY_NAME=My Company Inc.\n\n# Organization routing\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\n\n# Features (match your quickback.config.ts)\nENABLE_SIGNUP=true\nENABLE_ORGANIZATIONS=true\
|
|
62
|
+
"content": "When using Account UI with a Quickback-compiled backend, set the auth route mode to `quickback` to match Quickback's API structure. This is the recommended setup for Quickback projects.\n\n## Setup\n\nYou can use Account UI with Quickback as either a standalone template or an npm library.\n\n### Option A: Template (Clone the Source)\n\n#### 1. Clone the Template\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n#### 2. Set the Auth Route Mode\n\nConfigure Account UI to use Quickback's route conventions:\n\n```bash title=\".env\"\n# Use Quickback route mode\nVITE_AUTH_ROUTE=quickback\n\n# Your Quickback API URL\nVITE_API_URL=https://api.example.com\n\n# Where this Account UI is hosted\nVITE_ACCOUNT_APP_URL=https://account.example.com\n\n# Your main application URL (redirect after login)\nVITE_APP_URL=https://app.example.com\n```\n\n#### 3. Configure Your App\n\n```bash title=\".env\"\n# App identity\nVITE_APP_NAME=My App\nVITE_APP_TAGLINE=Build faster, ship sooner\nVITE_COMPANY_NAME=My Company Inc.\n\n# Organization routing\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\n\n# Features (match your quickback.config.ts)\nENABLE_SIGNUP=true\nENABLE_ORGANIZATIONS=true\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\nENABLE_ADMIN=true\n```\n\n#### 4. Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\n```\n\n### Option B: Library (npm install)\n\nInstall as a dependency in your existing React app:\n\n```bash\nnpm install quickback-better-auth-account-ui\n```\n\n```tsx title=\"src/main.tsx\"\n\nsetAppConfig({\n authRoute: 'quickback',\n name: 'My App',\n companyName: 'My Company Inc.',\n tagline: 'Build faster, ship sooner',\n});\n\n// Render AuthApp inside your router\n```\n\nFor a Cloudflare Worker, re-export the worker entry:\n\n```ts title=\"src/worker.ts\"\nexport { default } from 'quickback-better-auth-account-ui/worker';\n```\n\nSee [Library Usage](/account-ui/library-usage) for the complete guide.\n\n## How It Works\n\nIn Quickback mode, Account UI routes requests to three API paths:\n\n| Path | Purpose | Examples |\n|------|---------|---------|\n| `/auth/v1/*` | Authentication | Login, signup, sessions, passkeys |\n| `/api/v1/*` | Data API | Organizations, API keys |\n| `/storage/v1/*` | File storage | Avatar uploads, file management |\n\nThese paths match what the Quickback compiler generates in your backend's `src/index.ts`.\n\n## Architecture\n\nA typical Quickback deployment has three services:\n\n```\naccount.example.com → Account UI (this app)\napi.example.com → Quickback-compiled API\napp.example.com → Your main application\n```\n\nThe Account UI handles all authentication flows. After login, users are redirected to your main application at `VITE_APP_URL`. Organization-specific redirects use the `VITE_TENANT_URL_PATTERN`.\n\n## Matching Features to Config\n\nEnable Account UI features that match your `quickback.config.ts`:\n\n```typescript title=\"quickback/quickback.config.ts\"\nexport default defineConfig({\n features: [\"organizations\"], // → ENABLE_ORGANIZATIONS=true\n providers: {\n auth: {\n name: \"better-auth\",\n config: {\n plugins: [\n \"organization\", // → ENABLE_ORGANIZATIONS=true\n \"admin\", // → ENABLE_ADMIN=true\n \"apiKey\", // → API key UI available\n \"passkey\", // → ENABLE_PASSKEY=true\n \"emailOtp\", // → ENABLE_EMAIL_OTP=true\n \"deviceAuthorization\", // → CLI login flow available\n ],\n },\n },\n },\n});\n```\n\nIf your config doesn't include a plugin, disable the corresponding feature flag in Account UI to hide that section of the UI.\n\n## File Uploads\n\nWhen your Quickback project includes R2 file storage, enable avatar uploads:\n\n```bash\nVITE_ENABLE_FILE_UPLOADS=true\n```\n\nThis allows users to upload profile avatars via the storage API at `/storage/v1/object/avatars/`.\n\n## CLI Authorization\n\nIf your Quickback config includes the `deviceAuthorization` plugin, Account UI provides the `/cli/authorize` page. When users run `quickback login`, they're directed to this page to approve the CLI session.\n\nNo additional configuration is needed — the device authorization endpoints are generated automatically by the compiler.\n\n## Environment-Specific Setup\n\n### Development\n\n```bash title=\".env.development\"\nVITE_AUTH_ROUTE=quickback\nVITE_API_URL=http://localhost:8787\nVITE_ACCOUNT_APP_URL=http://localhost:5173\nVITE_APP_URL=http://localhost:3000\nDISABLE_EMAIL_STATUS_CHECK=true\n```\n\n### Production\n\n```bash title=\".env.production\"\nVITE_AUTH_ROUTE=quickback\nVITE_API_URL=https://api.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\nVITE_APP_URL=https://app.example.com\nENABLE_EMAIL_VERIFICATION=true\n```\n\n## Wrangler Configuration\n\nFor Cloudflare Workers deployment, set variables in `wrangler.toml`:\n\n```toml title=\"wrangler.toml\"\n[vars]\nVITE_AUTH_ROUTE = \"quickback\"\nVITE_API_URL = \"https://api.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account.example.com\"\nVITE_APP_URL = \"https://app.example.com\"\nVITE_APP_NAME = \"My App\"\nVITE_TENANT_URL_PATTERN = \"/organizations/{slug}\"\n```\n\n## Next Steps\n\n- [Environment Variables](/account-ui/environment-variables) — Complete variable reference\n- [Customization](/account-ui/customization) — Branding, labels, and theming\n- [Feature Flags](/account-ui/features) — Enable and disable features\n- [Worker Setup](/account-ui/worker) — Cloudflare Workers deployment details"
|
|
63
63
|
},
|
|
64
64
|
"account-ui/worker": {
|
|
65
65
|
"title": "Worker Setup",
|
|
@@ -123,7 +123,7 @@ export const DOCS = {
|
|
|
123
123
|
},
|
|
124
124
|
"compiler/cloud-compiler": {
|
|
125
125
|
"title": "Cloud Compiler",
|
|
126
|
-
"content": "Quickback offers a hosted compiler at `https://compiler.quickback.dev`. The CLI uses it to turn your definitions into a complete backend.\n\n## How It Works\n\n```\n┌──────────────┐ ┌──────────────────────┐\n│ quickback │ POST │ compiler.quickback │\n│ CLI │────────▶│ .dev/compile │\n│ │ │ │\n│ Sends: │ │ Returns: │\n│ - config │ │ - Hono routes │\n│ - features │◀────────│ - Drizzle migrations │\n│ - meta │ │ - TypeScript SDK │\n└──────────────┘ └──────────────────────┘\n```\n\n1. The CLI reads your `quickback.config.ts` and feature definitions.\n2. It sends them to the cloud compiler as a JSON payload.\n3. The compiler generates all backend files (routes, migrations, SDK, OpenAPI spec).\n4. The CLI writes the generated files to your project.\n\n## Quick Start\n\n```bash\n# 1. Install the CLI\nnpm install -g @kardoe/quickback\n\n# 2. Create a project\nquickback create cloudflare my-app\ncd my-app\n\n# 3. Log in\nquickback login\n\n# 4. Compile\nquickback compile\n```\n\n## Existing Databases\n\nWhen recompiling an existing project, the CLI automatically sends your Drizzle meta files so the compiler generates **incremental** migrations instead of fresh `CREATE TABLE` statements.\n\nMeta files are loaded from:\n- `drizzle/auth/meta/`\n- `drizzle/features/meta/`\n- `drizzle/files/meta/`\n- `drizzle/webhooks/meta/`\n- `drizzle/meta/` (
|
|
126
|
+
"content": "Quickback offers a hosted compiler at `https://compiler.quickback.dev`. The CLI uses it to turn your definitions into a complete backend.\n\n## How It Works\n\n```\n┌──────────────┐ ┌──────────────────────┐\n│ quickback │ POST │ compiler.quickback │\n│ CLI │────────▶│ .dev/compile │\n│ │ │ │\n│ Sends: │ │ Returns: │\n│ - config │ │ - Hono routes │\n│ - features │◀────────│ - Drizzle migrations │\n│ - meta │ │ - TypeScript SDK │\n└──────────────┘ └──────────────────────┘\n```\n\n1. The CLI reads your `quickback.config.ts` and feature definitions.\n2. It sends them to the cloud compiler as a JSON payload.\n3. The compiler generates all backend files (routes, migrations, SDK, OpenAPI spec).\n4. The CLI writes the generated files to your project.\n\n## Quick Start\n\n```bash\n# 1. Install the CLI\nnpm install -g @kardoe/quickback\n\n# 2. Create a project\nquickback create cloudflare my-app\ncd my-app\n\n# 3. Log in\nquickback login\n\n# 4. Compile\nquickback compile\n```\n\n## Existing Databases\n\nWhen recompiling an existing project, the CLI automatically sends your Drizzle meta files so the compiler generates **incremental** migrations instead of fresh `CREATE TABLE` statements.\n\nMeta files are loaded from:\n- `quickback/drizzle/auth/meta/`\n- `quickback/drizzle/features/meta/`\n- `quickback/drizzle/files/meta/`\n- `quickback/drizzle/webhooks/meta/`\n- `quickback/drizzle/meta/` (single database mode)\n- `drizzle/...` (legacy paths, checked as fallback)\n\nMigration and report artifacts are written to the `quickback/` state directory:\n- `quickback/drizzle/...` for Drizzle migration SQL/meta artifacts\n- `quickback/reports/...` for security contract report artifacts\n\nNo extra configuration needed — the CLI handles this automatically.\n\n## Next Steps\n\n- [CLI Reference](/compiler/cloud-compiler/cli) — All CLI commands\n- [Authentication](/compiler/cloud-compiler/authentication) — Login flow and API keys\n- [Endpoints](/compiler/cloud-compiler/endpoints) — Compiler API reference\n- [Troubleshooting](/compiler/cloud-compiler/troubleshooting) — Common issues\n- [Local Compiler](/compiler/cloud-compiler/local-compiler) — Run the compiler locally"
|
|
127
127
|
},
|
|
128
128
|
"compiler/cloud-compiler/local-compiler": {
|
|
129
129
|
"title": "Local Compiler",
|
|
@@ -135,11 +135,11 @@ export const DOCS = {
|
|
|
135
135
|
},
|
|
136
136
|
"compiler/config": {
|
|
137
137
|
"title": "Configuration",
|
|
138
|
-
"content": "The `quickback.config.ts` file configures your Quickback project. It defines which providers to use, which features to enable, and how the compiler generates code.\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n template: \"hono\",\n features: [\"organizations\"],\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\"),\n auth: defineAuth(\"better-auth\"),\n },\n});\n```\n\n## Required Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `name` | `string` | Project name |\n| `template` | `\"hono\"` | Application template (`\"nextjs\"` is experimental) |\n| `providers` | `object` | Runtime, database, and auth provider configuration |\n\n## Optional Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `features` | `string[]` | Feature flags, e.g. `[\"organizations\"]` |\n| `compiler` | `object` | Compiler options (including migration rename hints for headless CI) |\n| `openapi` | `object` | OpenAPI spec generation (`generate`, `publish`) — see [OpenAPI](/compiler/using-the-api/openapi) |\n| `schemaRegistry` | `object` | Schema registry generation for the CMS (enabled by default, `{ generate: false }` to disable) — see [Schema Registry](/cms/schema-registry) |\n\n## Headless Migration Rename Hints\n\nWhen `drizzle-kit generate` detects a potential rename, it normally prompts for confirmation. Cloud/CI compiles are non-interactive, so you must provide explicit rename hints.\n\nAdd hints in `compiler.migrations.renames`:\n\n```typescript\nexport default defineConfig({\n // ...\n compiler: {\n migrations: {\n renames: {\n // Keys are NEW names, values are OLD names\n tables: {\n events_v2: \"events\",\n },\n columns: {\n events: {\n summary_text: \"summary\",\n },\n },\n },\n },\n },\n});\n```\n\nRules:\n- `tables`: `new_table_name -> old_table_name`\n- `columns.<table>`: `new_column_name -> old_column_name`\n- Keys must match the names in your current schema (the new names)\n- Validation fails fast for malformed rename config, unsupported keys, or conflicting legacy/new rename paths.\n\n## Security Contract Reports and Signing\n\nAfter generation, Quickback verifies machine-checkable security contracts for Hono routes and RLS SQL.\n\nIt also emits a report artifact and signature artifact by default:\n\n- `reports/security-contracts.report.json`\n- `reports/security-contracts.report.sig.json`\n\nYou can configure output paths and signing behavior:\n\n```typescript\nexport default defineConfig({\n // ...\n compiler: {\n securityContracts: {\n failMode: \"error\", // or \"warn\"\n report: {\n path: \"reports/security-contracts.report.json\",\n signature: {\n enabled: true,\n required: true,\n keyEnv: \"QUICKBACK_SECURITY_REPORT_SIGNING_KEY\",\n keyId: \"prod-k1\",\n path: \"reports/security-contracts.report.sig.json\",\n },\n },\n },\n },\n});\n```\n\nIf `signature.required: true` and no key is available, compilation fails with a clear error message (or warning in `failMode: \"warn\"`).\n\nSee the sub-pages for detailed reference on each section:\n- [Providers](/compiler/config/providers) — Runtime, database, and auth options\n- [Variables](/compiler/config/variables) — Environment variables and flags\n- [Output](/compiler/config/output) — Generated file structure"
|
|
138
|
+
"content": "The `quickback.config.ts` file configures your Quickback project. It defines which providers to use, which features to enable, and how the compiler generates code.\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n template: \"hono\",\n features: [\"organizations\"],\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\"),\n auth: defineAuth(\"better-auth\"),\n },\n});\n```\n\n## Required Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `name` | `string` | Project name |\n| `template` | `\"hono\"` | Application template (`\"nextjs\"` is experimental) |\n| `providers` | `object` | Runtime, database, and auth provider configuration |\n\n## Optional Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `features` | `string[]` | Feature flags, e.g. `[\"organizations\"]` |\n| `build` | `object` | Build options (`outputDir`, `packageManager`, `dependencies`) |\n| `compiler` | `object` | Compiler options (including migration rename hints for headless CI) |\n| `openapi` | `object` | OpenAPI spec generation (`generate`, `publish`) — see [OpenAPI](/compiler/using-the-api/openapi) |\n| `schemaRegistry` | `object` | Schema registry generation for the CMS (enabled by default, `{ generate: false }` to disable) — see [Schema Registry](/cms/schema-registry) |\n\n## Custom Dependencies\n\nAdd third-party npm packages to the generated `package.json` using `build.dependencies`. This is useful when your action handlers import external libraries.\n\n```typescript\nexport default defineConfig({\n name: \"my-app\",\n template: \"hono\",\n build: {\n dependencies: {\n \"fast-xml-parser\": \"^4.5.0\",\n \"lodash-es\": \"^4.17.21\",\n },\n },\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\"),\n auth: defineAuth(\"better-auth\"),\n },\n});\n```\n\nThese are merged into the generated `package.json` alongside Quickback's own dependencies. Run `npm install` after compiling to install them.\n\n## Headless Migration Rename Hints\n\nWhen `drizzle-kit generate` detects a potential rename, it normally prompts for confirmation. Cloud/CI compiles are non-interactive, so you must provide explicit rename hints.\n\nAdd hints in `compiler.migrations.renames`:\n\n```typescript\nexport default defineConfig({\n // ...\n compiler: {\n migrations: {\n renames: {\n // Keys are NEW names, values are OLD names\n tables: {\n events_v2: \"events\",\n },\n columns: {\n events: {\n summary_text: \"summary\",\n },\n },\n },\n },\n },\n});\n```\n\nRules:\n- `tables`: `new_table_name -> old_table_name`\n- `columns.<table>`: `new_column_name -> old_column_name`\n- Keys must match the names in your current schema (the new names)\n- Validation fails fast for malformed rename config, unsupported keys, or conflicting legacy/new rename paths.\n\n## Security Contract Reports and Signing\n\nAfter generation, Quickback verifies machine-checkable security contracts for Hono routes and RLS SQL.\n\nIt also emits a report artifact and signature artifact by default:\n\n- `reports/security-contracts.report.json`\n- `reports/security-contracts.report.sig.json`\n\nYou can configure output paths and signing behavior:\n\n```typescript\nexport default defineConfig({\n // ...\n compiler: {\n securityContracts: {\n failMode: \"error\", // or \"warn\"\n report: {\n path: \"reports/security-contracts.report.json\",\n signature: {\n enabled: true,\n required: true,\n keyEnv: \"QUICKBACK_SECURITY_REPORT_SIGNING_KEY\",\n keyId: \"prod-k1\",\n path: \"reports/security-contracts.report.sig.json\",\n },\n },\n },\n },\n});\n```\n\nIf `signature.required: true` and no key is available, compilation fails with a clear error message (or warning in `failMode: \"warn\"`).\n\nSee the sub-pages for detailed reference on each section:\n- [Providers](/compiler/config/providers) — Runtime, database, and auth options\n- [Variables](/compiler/config/variables) — Environment variables and flags\n- [Output](/compiler/config/output) — Generated file structure"
|
|
139
139
|
},
|
|
140
140
|
"compiler/config/output": {
|
|
141
141
|
"title": "Output Structure",
|
|
142
|
-
"content": "The compiler generates a complete project structure based on your definitions and provider configuration. The output varies depending on your runtime (Cloudflare vs Bun) and enabled features.\n\n**Warning:** Never edit files in `src/` directly. They are overwritten on every compile. Make changes in your `quickback/` definitions instead.\n\n## Cloudflare Output\n\n```\nsrc/\n├── index.ts # Hono app entry point (Workers export)\n├── env.d.ts # Cloudflare bindings TypeScript types\n├── db/\n│ ├── index.ts # Database connection factory\n│ ├── auth-schema.ts # Auth table schemas (dual mode)\n│ └── features-schema.ts # Feature table schemas (dual mode)\n├── auth/\n│ └── index.ts # Better Auth instance & config\n├── features/\n│ └── {feature}/\n│ ├── schema.ts # Drizzle table definition\n│ ├── routes.ts # CRUD + action endpoints\n│ └── actions.ts # Action handlers (if defined)\n├── lib/\n│ ├── access.ts # Access control helpers\n│ ├── types.ts # Runtime type definitions\n│ ├── masks.ts # Field masking utilities\n│ ├── services.ts # Service layer\n│ ├── audit-wrapper.ts # Auto-inject audit fields\n│ └── security-audit.ts # Unsafe cross-tenant audit logger (when needed)\n└── middleware/\n ├── auth.ts # Auth context middleware\n ├── db.ts # Database instance middleware\n └── services.ts # Service injection middleware\n\
|
|
142
|
+
"content": "The compiler generates a complete project structure based on your definitions and provider configuration. The output varies depending on your runtime (Cloudflare vs Bun) and enabled features.\n\n**Warning:** Never edit files in `src/` directly. They are overwritten on every compile. Make changes in your `quickback/` definitions instead.\n\n## Cloudflare Output\n\n```\nsrc/\n├── index.ts # Hono app entry point (Workers export)\n├── env.d.ts # Cloudflare bindings TypeScript types\n├── db/\n│ ├── index.ts # Database connection factory\n│ ├── auth-schema.ts # Auth table schemas (dual mode)\n│ └── features-schema.ts # Feature table schemas (dual mode)\n├── auth/\n│ └── index.ts # Better Auth instance & config\n├── features/\n│ └── {feature}/\n│ ├── schema.ts # Drizzle table definition\n│ ├── routes.ts # CRUD + action endpoints\n│ └── actions.ts # Action handlers (if defined)\n├── lib/\n│ ├── access.ts # Access control helpers\n│ ├── types.ts # Runtime type definitions\n│ ├── masks.ts # Field masking utilities\n│ ├── services.ts # Service layer\n│ ├── audit-wrapper.ts # Auto-inject audit fields\n│ └── security-audit.ts # Unsafe cross-tenant audit logger (when needed)\n└── middleware/\n ├── auth.ts # Auth context middleware\n ├── db.ts # Database instance middleware\n └── services.ts # Service injection middleware\n\nquickback/drizzle/\n├── auth/ # Auth migrations (dual mode)\n│ ├── meta/\n│ │ ├── _journal.json\n│ │ └── 0000_snapshot.json\n│ └── 0000_initial.sql\n├── features/ # Feature migrations (dual mode)\n│ ├── meta/\n│ │ ├── _journal.json\n│ │ └── 0000_snapshot.json\n│ └── 0000_initial.sql\n└── audit/ # Unsafe cross-tenant action audit migrations (when needed)\n ├── meta/\n └── 0000_initial.sql\n\n# Root config files\n├── package.json\n├── tsconfig.json\n├── wrangler.toml # Cloudflare Workers config\n├── drizzle.config.ts # Features DB drizzle config\n├── drizzle.auth.config.ts # Auth DB drizzle config (dual mode)\n└── drizzle.audit.config.ts # Audit DB drizzle config (when unsafe actions exist)\n```\n\n## Bun Output\n\n```\nsrc/\n├── index.ts # Hono app entry point (Bun server)\n├── db/\n│ ├── index.ts # SQLite connection (bun:sqlite)\n│ └── schema.ts # Combined schema (single DB)\n├── auth/\n│ └── index.ts # Better Auth instance\n├── features/\n│ └── {feature}/\n│ ├── schema.ts\n│ ├── routes.ts\n│ └── actions.ts\n├── lib/\n│ ├── access.ts\n│ ├── types.ts\n│ ├── masks.ts\n│ ├── services.ts\n│ └── audit-wrapper.ts\n└── middleware/\n ├── auth.ts\n ├── db.ts\n └── services.ts\n\nquickback/drizzle/ # Single migration directory\n├── meta/\n│ ├── _journal.json\n│ └── 0000_snapshot.json\n└── 0000_initial.sql\n\ndata/ # SQLite database files\n└── app.db\n\n├── package.json\n├── tsconfig.json\n└── drizzle.config.ts\n```\n\n## Key Differences by Runtime\n\n| Aspect | Cloudflare | Bun |\n|--------|-----------|-----|\n| Entry point | Workers `export default` | `Bun.serve()` with port |\n| Database | D1 bindings (dual mode) | SQLite file (single DB) |\n| Types | `env.d.ts` for bindings | No extra types needed |\n| Config | `wrangler.toml` | `.env` file |\n| Migrations | `quickback/drizzle/auth/` + `quickback/drizzle/features/` | `quickback/drizzle/` |\n| Drizzle configs | 2 configs (auth + features) | 1 config |\n\n## Optional Output Files\n\nThese files are generated only when the corresponding features are configured:\n\n### Embeddings\n\nWhen any feature has `embeddings` configured:\n\n```\nsrc/\n├── lib/\n│ └── embeddings.ts # Embedding helpers\n├── routes/\n│ └── embeddings.ts # POST /api/v1/embeddings endpoint\n└── queue-consumer.ts # Queue handler for async embedding jobs\n```\n\n### File Storage (R2)\n\nWhen `fileStorage` is configured:\n\n```\nsrc/\n└── routes/\n └── storage.ts # File upload/download endpoints\nquickback/drizzle/\n└── files/ # File metadata migrations\n ├── meta/\n └── 0000_*.sql\n```\n\n### Webhooks\n\nWhen webhooks are enabled:\n\n```\nsrc/\n└── lib/\n └── webhooks/\n ├── index.ts # Webhook module entry\n ├── sign.ts # Webhook payload signing\n ├── handlers.ts # Handler registry\n ├── emit.ts # Queue emission helpers\n ├── routes.ts # Inbound/outbound endpoints\n └── providers/\n ├── index.ts\n └── stripe.ts # Stripe webhook handler\nquickback/drizzle/\n└── webhooks/ # Webhook schema migrations\n ├── meta/\n └── 0000_*.sql\n```\n\n### Security Audit Database (Unsafe Actions)\n\nWhen any action enables unsafe cross-tenant mode (`unsafe.crossTenant: true`):\n\n```\nsrc/\n├── db/\n│ └── audit-schema.ts # audit_events table\n└── lib/\n └── security-audit.ts # mandatory audit writer\n\nquickback/drizzle/\n└── audit/\n ├── meta/\n └── 0000_*.sql\n\n# Root\n└── drizzle.audit.config.ts\n```\n\nCloudflare output also includes an `AUDIT_DB` D1 binding in `wrangler.toml` and migration scripts:\n\n- `db:migrate:audit:local`\n- `db:migrate:audit:remote`\n\n### Security Contract Report and Signature\n\nGenerated on every compile (unless disabled via `compiler.securityContracts.report.enabled: false`):\n\n```\nreports/\n├── security-contracts.report.json # Contract evaluation summary + violations\n└── security-contracts.report.sig.json # Signature / digest envelope for the report\n```\n\nThe signature file uses HMAC-SHA256 when a signing key is configured, otherwise it falls back to SHA-256 digest mode. \nSet `compiler.securityContracts.report.signature.required: true` to fail compilation when a signing key is missing.\n\n### Realtime\n\nWhen any feature has `realtime` configured:\n\n```\nsrc/\n└── lib/\n └── realtime.ts # Broadcast helpers\n\ncloudflare-workers/ # Separate Durable Objects worker\n└── broadcast/\n ├── Broadcaster.ts\n ├── index.ts\n └── wrangler.toml\n```\n\n### Device Authorization\n\nWhen the `deviceAuthorization` plugin is enabled:\n\n```\nsrc/\n└── routes/\n └── cli-auth.ts # Device auth flow endpoints\n```\n\n## Database Schemas\n\n### Dual Database Mode (Cloudflare Default)\n\nThe compiler separates schemas into two files:\n\n**`src/db/auth-schema.ts`** — Re-exports Better Auth table schemas:\n- `users`, `sessions`, `accounts`\n- `organizations`, `members`, `invitations` (if organizations enabled)\n- Plugin-specific tables (`apiKeys`, etc.)\n\n**`src/db/features-schema.ts`** — Re-exports your feature schemas:\n- All tables defined with `defineTable()`\n- Audit field columns added automatically\n\n### Single Database Mode (Bun Default)\n\n**`src/db/schema.ts`** — Combined re-export of all schemas (auth + features).\n\n## Generated Routes\n\nFor each feature, the compiler generates a routes file at `src/features/{name}/routes.ts` containing:\n\n| Route | Generated When |\n|-------|----------------|\n| `GET /` | `crud.list` configured |\n| `GET /:id` | `crud.get` configured |\n| `POST /` | `crud.create` configured |\n| `PATCH /:id` | `crud.update` configured |\n| `DELETE /:id` | `crud.delete` configured |\n| `PUT /:id` | `crud.put` configured |\n| `POST /batch` | `crud.create` exists (auto-enabled) |\n| `PATCH /batch` | `crud.update` exists (auto-enabled) |\n| `DELETE /batch` | `crud.delete` exists (auto-enabled) |\n| `PUT /batch` | `crud.put` exists (auto-enabled) |\n| `GET /views/{name}` | `views` configured |\n| `POST /:id/{action}` | Record-based actions defined |\n| `POST /{action}` | Standalone actions defined |\n\nAll routes are mounted under `/api/v1/{feature}` in the main app.\n\n## Migrations\n\nThe compiler runs `drizzle-kit generate` during compilation to produce SQL migration files. On subsequent compiles, it uses `existingFiles` (your current migration state) to generate only incremental changes.\n\nThe CLI loads migration meta JSON from:\n\n- `quickback/drizzle/auth/meta/`\n- `quickback/drizzle/features/meta/`\n- `quickback/drizzle/files/meta/`\n- `quickback/drizzle/webhooks/meta/`\n- `quickback/drizzle/meta/` (single database mode)\n- `drizzle/...` (legacy paths, checked as fallback)\n\nMigration and report artifacts are written to the `quickback/` state directory:\n\n- `quickback/drizzle/...` for Drizzle migration SQL/meta artifacts\n- `quickback/reports/...` for security contract report artifacts\n\nFor non-interactive environments (cloud compile, CI), table/column renames must be declared with `compiler.migrations.renames` in `quickback.config.ts`. This avoids interactive rename prompts during migration generation.\n\nMigration files follow the Drizzle Kit naming convention:\n```\n0000_initial.sql\n0001_add_status_column.sql\n0002_create_orders_table.sql\n```\n\n## See Also\n\n- [Providers](/compiler/config/providers) — Configure runtime, database, and auth providers\n- [Environment variables](/compiler/config/variables) — Required variables by runtime"
|
|
143
143
|
},
|
|
144
144
|
"compiler/config/providers": {
|
|
145
145
|
"title": "Providers",
|
|
@@ -155,7 +155,7 @@ export const DOCS = {
|
|
|
155
155
|
},
|
|
156
156
|
"compiler/definitions/actions": {
|
|
157
157
|
"title": "Actions",
|
|
158
|
-
"content": "Actions are custom API endpoints for business logic beyond CRUD operations. They enable workflows, integrations, and complex operations.\n\n## Overview\n\nQuickback supports two types of actions:\n\n| Aspect | Record-Based | Standalone |\n|--------|--------------|------------|\n| Route | `{METHOD} /:id/{actionName}` | Custom `path` or `/{actionName}` |\n| Record fetching | Automatic | None (`record` is `undefined`) |\n| Firewall applied | Yes | No |\n| Preconditions | Supported via `access.record` | Not applicable |\n| Response types | JSON only | JSON, stream, file |\n| Use case | Advance application, reject candidate | AI chat, bulk import from job board, webhooks |\n\n## Defining Actions\n\nActions are defined in a separate `actions.ts` file that references your table:\n\n```typescript\n// features/applications/actions.ts\n\nexport default defineActions(applications, {\n 'advance-stage': {\n description: \"Move application to the next pipeline stage\",\n input: z.object({ stage: z.enum([\"screening\", \"interview\", \"offer\", \"hired\"]), notes: z.string().optional() }),\n access: { roles: [\"owner\", \"hiring-manager\"] },\n execute: async ({ db, record, ctx }) => {\n // Business logic\n return record;\n },\n },\n});\n```\n\n### Configuration Options\n\n| Option | Required | Description |\n|--------|----------|-------------|\n| `description` | Yes | Human-readable description of the action |\n| `input` | Yes | Zod schema for request validation |\n| `access` | Yes | Access control (roles, record conditions, or function) |\n| `execute` | Yes* | Inline execution function |\n| `handler` | Yes* | File path for complex logic (alternative to `execute`) |\n| `standalone` | No | Set `true` for non-record actions |\n| `path` | No | Custom route path (standalone only) |\n| `method` | No | HTTP method: GET, POST, PUT, PATCH, DELETE (default: POST) |\n| `responseType` | No | Response format: json, stream, file (default: json) |\n| `sideEffects` | No | Hint for AI tools: 'sync', 'async', or 'fire-and-forget' |\n| `allowRawSql` | No | Explicit compile-time opt-in for raw SQL in execute/handler code |\n| `unsafe` | No | Unsafe raw DB mode. Use `true` (legacy) or object config (`reason`, `adminOnly`, `crossTenant`, `targetScope`) |\n\n*Either `execute` or `handler` is required, not both.\n\n## Record-Based Actions\n\nRecord-based actions operate on an existing record. The record is automatically loaded and validated before your action executes.\n\n**Route pattern:** `{METHOD} /:id/{actionName}`\n\n```\nPOST /applications/:id/advance-stage\nPOST /applications/:id/reject\nPOST /applications/:id/schedule-interview\n```\n\n### Runtime Flow\n\n1. **Authentication** - User token is validated\n2. **Record Loading** - The record is fetched by ID\n3. **Firewall Check** - Ensures user can access this record\n4. **Access Check** - Validates roles and preconditions\n5. **Input Validation** - Request body validated against Zod schema\n6. **Execution** - Your action handler runs\n7. **Response** - Result is returned to client\n\n### Example: Advance Application Stage\n\n```typescript\n// features/applications/actions.ts\n\nexport default defineActions(applications, {\n 'advance-stage': {\n description: \"Move application to the next pipeline stage\",\n input: z.object({\n stage: z.enum([\"screening\", \"interview\", \"offer\", \"hired\"]),\n notes: z.string().optional(),\n }),\n access: {\n roles: [\"owner\", \"hiring-manager\"],\n record: { stage: { notEquals: \"rejected\" } }, // Precondition\n },\n execute: async ({ db, ctx, record, input }) => {\n const [updated] = await db\n .update(applications)\n .set({\n stage: input.stage,\n notes: input.notes ?? record.notes,\n })\n .where(eq(applications.id, record.id))\n .returning();\n\n return updated;\n },\n },\n});\n```\n\n### Request Example\n\n```\nPOST /applications/app_123/advance-stage\nContent-Type: application/json\n\n{\n \"stage\": \"interview\",\n \"notes\": \"Strong technical background, moving to interview\"\n}\n```\n\n### Response Example\n\n```json\n{\n \"data\": {\n \"id\": \"app_123\",\n \"stage\": \"interview\",\n \"notes\": \"Strong technical background, moving to interview\"\n }\n}\n```\n\n## Standalone Actions\n\nStandalone actions are independent endpoints that don't require a record context. Use `standalone: true` and optionally specify a custom `path`.\n\n**Route pattern:** Custom `path` or `/{actionName}`\n\n```\nPOST /chat\nGET /reports/summary\nPOST /webhooks/stripe\n```\n\n### Example: AI Chat with Streaming\n\n```typescript\nexport default defineActions(sessions, {\n chat: {\n description: \"Send a message to AI\",\n standalone: true,\n path: \"/chat\",\n method: \"POST\",\n responseType: \"stream\",\n input: z.object({\n message: z.string().min(1).max(2000),\n }),\n access: {\n roles: [\"recruiter\", \"hiring-manager\"],\n },\n handler: \"./handlers/chat\",\n },\n});\n```\n\n### Streaming Response Example\n\nFor actions with `responseType: 'stream'`:\n\n```\nContent-Type: text/event-stream\n\ndata: {\"type\": \"start\"}\ndata: {\"type\": \"chunk\", \"content\": \"Hello\"}\ndata: {\"type\": \"chunk\", \"content\": \"! I'm\"}\ndata: {\"type\": \"chunk\", \"content\": \" here to help.\"}\ndata: {\"type\": \"done\"}\n```\n\n### Example: Report Generation\n\n```typescript\nexport default defineActions(applications, {\n generateReport: {\n description: \"Generate hiring pipeline report\",\n standalone: true,\n path: \"/applications/report\",\n method: \"GET\",\n responseType: \"file\",\n input: z.object({\n startDate: z.string().datetime(),\n endDate: z.string().datetime(),\n }),\n access: { roles: [\"owner\", \"hiring-manager\"] },\n handler: \"./handlers/generate-report\",\n },\n});\n```\n\n## Access Configuration\n\nAccess controls who can execute an action and under what conditions.\n\n### Role-Based Access\n\n```typescript\naccess: {\n roles: [\"hiring-manager\", \"recruiter\"] // OR logic: user needs any of these roles\n}\n```\n\n### Record Conditions\n\nFor record-based actions, you can require the record to be in a specific state:\n\n```typescript\naccess: {\n roles: [\"hiring-manager\"],\n record: {\n stage: { equals: \"screening\" } // Precondition\n }\n}\n```\n\n**Supported operators:**\n\n| Operator | Description |\n|----------|-------------|\n| `equals` | Field must equal value |\n| `notEquals` | Field must not equal value |\n| `in` | Field must be one of the values |\n| `notIn` | Field must not be one of the values |\n\n### Context Substitution\n\nUse `$ctx` to reference the current user's context:\n\n```typescript\naccess: {\n record: {\n ownerId: { equals: \"$ctx.userId\" },\n orgId: { equals: \"$ctx.orgId\" }\n }\n}\n```\n\n### OR/AND Combinations\n\n```typescript\naccess: {\n or: [\n { roles: [\"hiring-manager\"] },\n {\n roles: [\"recruiter\"],\n record: { ownerId: { equals: \"$ctx.userId\" } }\n }\n ]\n}\n```\n\n### Function Access\n\nFor complex logic, use an access function:\n\n```typescript\naccess: async (ctx, record) => {\n return ctx.roles.includes('admin') || record.ownerId === ctx.userId;\n}\n```\n\n## Scoped Database\n\nAll actions receive a **scoped `db`** instance that automatically enforces security:\n\n| Operation | Org Scoping | Owner Scoping | Soft Delete Filter | Auto-inject on INSERT |\n|-----------|-------------|---------------|--------------------|-----------------------|\n| `SELECT` | `WHERE organizationId = ?` | `WHERE ownerId = ?` | `WHERE deletedAt IS NULL` | n/a |\n| `INSERT` | n/a | n/a | n/a | `organizationId`, `ownerId` from ctx |\n| `UPDATE` | `WHERE organizationId = ?` | `WHERE ownerId = ?` | `WHERE deletedAt IS NULL` | n/a |\n| `DELETE` | `WHERE organizationId = ?` | `WHERE ownerId = ?` | `WHERE deletedAt IS NULL` | n/a |\n\nScoping is duck-typed at runtime — tables with an `organizationId` column get org scoping, tables with `ownerId` get owner scoping, tables with `deletedAt` get soft delete filtering.\n\n```typescript\nexecute: async ({ db, ctx, input }) => {\n // This query automatically includes WHERE organizationId = ? AND deletedAt IS NULL\n const items = await db.select().from(applications).where(eq(applications.stage, 'interview'));\n\n // Inserts automatically include organizationId\n await db.insert(applications).values({ candidateId: input.candidateId, jobId: input.jobId, stage: 'applied' });\n\n return items;\n}\n```\n\n**Not enforced** in scoped DB (by design):\n- **Guards** — actions ARE the authorized way to modify guarded fields\n- **Masking** — actions are backend code that may need raw data\n- **Access** — already checked before action execution\n\n### Unsafe Mode\n\nActions that need to bypass scoped DB filters (for example, platform-level support operations) can enable unsafe mode.\n\nUse object form for explicit policy and mandatory audit metadata:\n\n```typescript\nexport default defineActions(applications, {\n adminReport: {\n description: \"Generate cross-org hiring report\",\n unsafe: {\n reason: \"Support investigation for enterprise customer\",\n adminOnly: true, // default true\n crossTenant: true, // default true\n targetScope: \"all\", // \"all\" | \"organization\"\n },\n input: z.object({ startDate: z.string() }),\n access: { roles: [\"owner\"] },\n execute: async ({ db, rawDb, ctx, input }) => {\n // db is still scoped (safety net)\n // rawDb bypasses scoped filters\n const allOrgs = await rawDb.select().from(applications);\n return allOrgs;\n },\n },\n});\n```\n\nUnsafe cross-tenant actions are generated with:\n\n- Better Auth authentication required (no unauthenticated path)\n- platform admin gate (`ctx.userRole === \"admin\"`)\n- mandatory audit logging on deny/success/error\n\nWithout unsafe mode, `rawDb` is `undefined` in the executor params.\n\nLegacy `unsafe: true` is still supported, but object mode is recommended so audit logs include an explicit reason.\n\n### Raw SQL Policy\n\nBy default, the compiler rejects raw SQL in action code and handler files. Use Drizzle query-builder syntax whenever possible.\n\nIf a specific action must use raw SQL, opt in explicitly:\n\n```typescript\nreconcileLedger: {\n description: \"Run custom reconciliation query\",\n allowRawSql: true,\n input: z.object({}),\n access: { roles: [\"owner\"] },\n execute: async ({ db }) => {\n // Allowed because allowRawSql: true\n return db.execute(sql`select 1`);\n },\n}\n```\n\nWithout `allowRawSql: true`, compilation fails with a loud error pointing to the action and snippet.\n\n## Handler Files\n\nFor complex actions, separate the logic into handler files.\n\n### When to Use Handler Files\n\n- Complex business logic spanning multiple operations\n- External API integrations\n- File generation or processing\n- Logic reused across multiple actions\n\n### Handler Structure\n\n```typescript\n// handlers/generate-report.ts\n\nexport const execute: ActionExecutor = async ({ db, ctx, input, services }) => {\n const apps = await db\n .select()\n .from(applicationsTable)\n .where(between(applicationsTable.createdAt, input.startDate, input.endDate));\n\n const pdf = await services.pdf.generate(apps);\n\n return {\n file: pdf,\n filename: `hiring-report-${input.startDate}-${input.endDate}.pdf`,\n contentType: 'application/pdf',\n };\n};\n```\n\n### Importing Tables\n\nHandler files can import tables from their own feature or other features. The compiler generates alias files for each table, so you import by the table's file name:\n\n```typescript\n// handlers/advance-stage.ts\n\n// Same feature — import from parent directory using the table's file name\n\n// Cross-feature — go up to the features directory, then into the other feature\n\nexport const execute: ActionExecutor = async ({ db, ctx, input, c }) => {\n const [job] = await db\n .select()\n .from(jobs)\n .where(eq(jobs.id, input.jobId))\n .limit(1);\n\n if (!job) {\n return c.json({ error: 'Job not found', code: 'NOT_FOUND' }, 404);\n }\n\n // ... continue with business logic\n};\n```\n\n**Path pattern from `features/{name}/handlers/`:**\n- Same feature table: `../{table-file-name}` (e.g., `../applications`)\n- Other feature table: `../../{other-feature}/{table-file-name}` (e.g., `../../candidates/candidates`)\n- Generated lib files: `../../../lib/{module}` (e.g., `../../../lib/realtime`, `../../../lib/webhooks`)\n\n### Executor Parameters\n\n```typescript\ninterface ActionExecutorParams {\n db: DrizzleDB; // Scoped database (auto-applies org/owner/soft-delete filters)\n rawDb?: DrizzleDB; // Raw database (only available when unsafe mode is enabled)\n ctx: AppContext; // User context (userId, roles, orgId)\n record?: TRecord; // The record (record-based only, undefined for standalone)\n input: TInput; // Validated input from Zod schema\n services: TServices; // Configured integrations (billing, notifications, etc.)\n c: HonoContext; // Raw Hono context for advanced use\n auditFields: object; // { createdAt, modifiedAt } timestamps\n}\n```\n\n## HTTP API Reference\n\n### Request Format\n\n| Method | Input Source | Use Case |\n|--------|--------------|----------|\n| `GET` | Query parameters | Read-only operations, fetching data |\n| `POST` | JSON body | Default, state-changing operations |\n| `PUT` | JSON body | Full replacement operations |\n| `PATCH` | JSON body | Partial updates |\n| `DELETE` | JSON body | Deletion with optional payload |\n\n```typescript\n// GET action - input comes from query params\ngetStageHistory: {\n method: \"GET\",\n input: z.object({ format: z.string().optional() }),\n // Called as: GET /applications/:id/getStageHistory?format=detailed\n}\n\n// POST action (default) - input comes from JSON body\n'advance-stage': {\n // method: \"POST\" is implied\n input: z.object({ stage: z.enum([\"screening\", \"interview\", \"offer\", \"hired\"]) }),\n // Called as: POST /applications/:id/advance-stage with JSON body\n}\n```\n\n### Response Formats\n\n| Type | Content-Type | Use Case |\n|------|--------------|----------|\n| `json` | `application/json` | Standard API responses (default) |\n| `stream` | `text/event-stream` | Real-time streaming (AI chat, live updates) |\n| `file` | Varies | File downloads (reports, exports) |\n\n### Error Codes\n\n| Status | Description |\n|--------|-------------|\n| `400` | Invalid input / validation error |\n| `401` | Not authenticated |\n| `403` | Access check failed (role or precondition) |\n| `404` | Record not found (record-based actions) |\n| `500` | Handler execution error |\n\n### Validation Error Response\n\n```json\n{\n \"error\": \"Invalid request data\",\n \"layer\": \"validation\",\n \"code\": \"VALIDATION_FAILED\",\n \"details\": {\n \"fields\": {\n \"amount\": \"Expected positive number\"\n }\n },\n \"hint\": \"Check the input schema for this action\"\n}\n```\n\n### Error Handling\n\n**Option 1: Return a JSON error response** (recommended for most cases)\n\nSince action handlers receive the Hono context (`c`), you can return error responses directly:\n\n```typescript\nexecute: async ({ ctx, record, input, c }) => {\n if (record.stage === 'hired') {\n return c.json({\n error: 'Cannot modify a hired application',\n code: 'ALREADY_HIRED',\n details: { currentStage: record.stage },\n }, 400);\n }\n // ... continue\n}\n```\n\n**Option 2: Throw an ActionError**\n\n```typescript\n\nexecute: async ({ ctx, record, input }) => {\n if (record.stage === 'hired') {\n throw new ActionError('Cannot modify a hired application', 'ALREADY_HIRED', 400, {\n currentStage: record.stage,\n });\n }\n // ... continue\n}\n```\n\nThe `ActionError` constructor signature is `(message, code, statusCode, details?)`.\n\n## Protected Fields\n\nActions can modify fields that are protected from regular CRUD operations:\n\n```typescript\n// In resource.ts\nguards: {\n protected: {\n stage: [\"advance-stage\", \"reject\"], // Only these actions can modify stage\n }\n}\n```\n\nThis allows the `advance-stage` action to set `stage = \"interview\"` even though the field is protected from regular PATCH requests.\n\n## Examples\n\n### Application Stage Advance (Record-Based)\n\n```typescript\nexport default defineActions(applications, {\n 'advance-stage': {\n description: \"Move application to the next pipeline stage\",\n input: z.object({\n stage: z.enum([\"screening\", \"interview\", \"offer\", \"hired\"]),\n notes: z.string().optional(),\n }),\n access: {\n roles: [\"owner\", \"hiring-manager\"],\n record: { stage: { notEquals: \"rejected\" } },\n },\n execute: async ({ db, ctx, record, input }) => {\n const [updated] = await db\n .update(applications)\n .set({\n stage: input.stage,\n notes: input.notes ?? record.notes,\n })\n .where(eq(applications.id, record.id))\n .returning();\n return updated;\n },\n },\n});\n```\n\n### AI Chat (Standalone with Streaming)\n\n```typescript\nexport default defineActions(sessions, {\n chat: {\n description: \"Send a message to AI assistant\",\n standalone: true,\n path: \"/chat\",\n responseType: \"stream\",\n input: z.object({\n message: z.string().min(1).max(2000),\n }),\n access: { roles: [\"recruiter\", \"hiring-manager\"] },\n handler: \"./handlers/chat\",\n },\n});\n```\n\n### Bulk Import (Standalone, No Record)\n\n```typescript\nexport default defineActions(candidates, {\n bulkImport: {\n description: \"Import candidates from job board CSV\",\n standalone: true,\n path: \"/candidates/import\",\n input: z.object({\n data: z.array(z.object({\n email: z.string().email(),\n name: z.string(),\n })),\n }),\n access: { roles: [\"hiring-manager\", \"recruiter\"] },\n execute: async ({ db, input }) => {\n const inserted = await db\n .insert(candidates)\n .values(input.data)\n .returning();\n return { imported: inserted.length };\n },\n },\n});\n```"
|
|
158
|
+
"content": "Actions are custom API endpoints for business logic beyond CRUD operations. They enable workflows, integrations, and complex operations.\n\n## Overview\n\nQuickback supports two types of actions:\n\n| Aspect | Record-Based | Standalone |\n|--------|--------------|------------|\n| Route | `{METHOD} /:id/{actionName}` | Custom `path` or `/{actionName}` |\n| Record fetching | Automatic | None (`record` is `undefined`) |\n| Firewall applied | Yes | No |\n| Preconditions | Supported via `access.record` | Not applicable |\n| Response types | JSON only | JSON, stream, file |\n| Use case | Advance application, reject candidate | AI chat, bulk import from job board, webhooks |\n\n## Defining Actions\n\nActions are defined in a separate `actions.ts` file that references your table:\n\n```typescript\n// features/applications/actions.ts\n\nexport default defineActions(applications, {\n 'advance-stage': {\n description: \"Move application to the next pipeline stage\",\n input: z.object({ stage: z.enum([\"screening\", \"interview\", \"offer\", \"hired\"]), notes: z.string().optional() }),\n access: { roles: [\"owner\", \"hiring-manager\"] },\n execute: async ({ db, record, ctx }) => {\n // Business logic\n return record;\n },\n },\n});\n```\n\n### Configuration Options\n\n| Option | Required | Description |\n|--------|----------|-------------|\n| `description` | Yes | Human-readable description of the action |\n| `input` | Yes | Zod schema for request validation |\n| `access` | Yes | Access control (roles, record conditions, or function) |\n| `execute` | Yes* | Inline execution function |\n| `handler` | Yes* | File path for complex logic (alternative to `execute`) |\n| `standalone` | No | Set `true` for non-record actions |\n| `path` | No | Custom route path (standalone only) |\n| `method` | No | HTTP method: GET, POST, PUT, PATCH, DELETE (default: POST) |\n| `responseType` | No | Response format: json, stream, file (default: json) |\n| `sideEffects` | No | Hint for AI tools: 'sync', 'async', or 'fire-and-forget' |\n| `allowRawSql` | No | Explicit compile-time opt-in for raw SQL in execute/handler code |\n| `unsafe` | No | Unsafe raw DB mode. Use `true` (legacy) or object config (`reason`, `adminOnly`, `crossTenant`, `targetScope`) |\n\n*Either `execute` or `handler` is required, not both.\n\n## Record-Based Actions\n\nRecord-based actions operate on an existing record. The record is automatically loaded and validated before your action executes.\n\n**Route pattern:** `{METHOD} /:id/{actionName}`\n\n```\nPOST /applications/:id/advance-stage\nPOST /applications/:id/reject\nPOST /applications/:id/schedule-interview\n```\n\n### Runtime Flow\n\n1. **Authentication** - User token is validated\n2. **Record Loading** - The record is fetched by ID\n3. **Firewall Check** - Ensures user can access this record\n4. **Access Check** - Validates roles and preconditions\n5. **Input Validation** - Request body validated against Zod schema\n6. **Execution** - Your action handler runs\n7. **Response** - Result is returned to client\n\n### Example: Advance Application Stage\n\n```typescript\n// features/applications/actions.ts\n\nexport default defineActions(applications, {\n 'advance-stage': {\n description: \"Move application to the next pipeline stage\",\n input: z.object({\n stage: z.enum([\"screening\", \"interview\", \"offer\", \"hired\"]),\n notes: z.string().optional(),\n }),\n access: {\n roles: [\"owner\", \"hiring-manager\"],\n record: { stage: { notEquals: \"rejected\" } }, // Precondition\n },\n execute: async ({ db, ctx, record, input }) => {\n const [updated] = await db\n .update(applications)\n .set({\n stage: input.stage,\n notes: input.notes ?? record.notes,\n })\n .where(eq(applications.id, record.id))\n .returning();\n\n return updated;\n },\n },\n});\n```\n\n### Request Example\n\n```\nPOST /applications/app_123/advance-stage\nContent-Type: application/json\n\n{\n \"stage\": \"interview\",\n \"notes\": \"Strong technical background, moving to interview\"\n}\n```\n\n### Response Example\n\n```json\n{\n \"data\": {\n \"id\": \"app_123\",\n \"stage\": \"interview\",\n \"notes\": \"Strong technical background, moving to interview\"\n }\n}\n```\n\n## Standalone Actions\n\nStandalone actions are independent endpoints that don't require a record context. Use `standalone: true` and optionally specify a custom `path`.\n\n**Route pattern:** Custom `path` or `/{actionName}`\n\n```\nPOST /chat\nGET /reports/summary\nPOST /webhooks/stripe\n```\n\n### Example: AI Chat with Streaming\n\n```typescript\nexport default defineActions(sessions, {\n chat: {\n description: \"Send a message to AI\",\n standalone: true,\n path: \"/chat\",\n method: \"POST\",\n responseType: \"stream\",\n input: z.object({\n message: z.string().min(1).max(2000),\n }),\n access: {\n roles: [\"recruiter\", \"hiring-manager\"],\n },\n handler: \"./handlers/chat\",\n },\n});\n```\n\n### Streaming Response Example\n\nFor actions with `responseType: 'stream'`:\n\n```\nContent-Type: text/event-stream\n\ndata: {\"type\": \"start\"}\ndata: {\"type\": \"chunk\", \"content\": \"Hello\"}\ndata: {\"type\": \"chunk\", \"content\": \"! I'm\"}\ndata: {\"type\": \"chunk\", \"content\": \" here to help.\"}\ndata: {\"type\": \"done\"}\n```\n\n### Example: Report Generation\n\n```typescript\nexport default defineActions(applications, {\n generateReport: {\n description: \"Generate hiring pipeline report\",\n standalone: true,\n path: \"/applications/report\",\n method: \"GET\",\n responseType: \"file\",\n input: z.object({\n startDate: z.string().datetime(),\n endDate: z.string().datetime(),\n }),\n access: { roles: [\"owner\", \"hiring-manager\"] },\n handler: \"./handlers/generate-report\",\n },\n});\n```\n\n### Actions-Only Features\n\nYou can create feature directories that contain **only standalone actions** — no table definitions required. This is useful for utility endpoints like reports, integrations, or webhooks that don't map to a specific resource.\n\n```\nquickback/features/\n└── reports/\n ├── actions.ts # Standalone actions only (no table file)\n └── handlers/\n ├── trial-balance.ts\n └── profit-loss.ts\n```\n\n```typescript\n// features/reports/actions.ts\nexport default defineActions(null, {\n trialBalance: {\n description: \"Generate trial balance report\",\n standalone: true,\n path: \"/reports/trial-balance\",\n method: \"GET\",\n input: z.object({\n startDate: z.string(),\n endDate: z.string(),\n }),\n access: { roles: [\"admin\", \"accountant\"] },\n handler: \"./handlers/trial-balance\",\n },\n});\n```\n\nPass `null` as the schema argument since there is no table. All actions in a tableless feature **must** have `standalone: true` — record-based actions require a table to operate on.\n\n## Access Configuration\n\nAccess controls who can execute an action and under what conditions.\n\n### Role-Based Access\n\n```typescript\naccess: {\n roles: [\"hiring-manager\", \"recruiter\"] // OR logic: user needs any of these roles\n}\n```\n\n### Record Conditions\n\nFor record-based actions, you can require the record to be in a specific state:\n\n```typescript\naccess: {\n roles: [\"hiring-manager\"],\n record: {\n stage: { equals: \"screening\" } // Precondition\n }\n}\n```\n\n**Supported operators:**\n\n| Operator | Description |\n|----------|-------------|\n| `equals` | Field must equal value |\n| `notEquals` | Field must not equal value |\n| `in` | Field must be one of the values |\n| `notIn` | Field must not be one of the values |\n\n### Context Substitution\n\nUse `$ctx` to reference the current user's context:\n\n```typescript\naccess: {\n record: {\n ownerId: { equals: \"$ctx.userId\" },\n orgId: { equals: \"$ctx.orgId\" }\n }\n}\n```\n\n### OR/AND Combinations\n\n```typescript\naccess: {\n or: [\n { roles: [\"hiring-manager\"] },\n {\n roles: [\"recruiter\"],\n record: { ownerId: { equals: \"$ctx.userId\" } }\n }\n ]\n}\n```\n\n### Function Access\n\nFor complex logic, use an access function:\n\n```typescript\naccess: async (ctx, record) => {\n return ctx.roles.includes('admin') || record.ownerId === ctx.userId;\n}\n```\n\n## Scoped Database\n\nAll actions receive a **scoped `db`** instance that automatically enforces security:\n\n| Operation | Org Scoping | Owner Scoping | Soft Delete Filter | Auto-inject on INSERT |\n|-----------|-------------|---------------|--------------------|-----------------------|\n| `SELECT` | `WHERE organizationId = ?` | `WHERE ownerId = ?` | `WHERE deletedAt IS NULL` | n/a |\n| `INSERT` | n/a | n/a | n/a | `organizationId`, `ownerId` from ctx |\n| `UPDATE` | `WHERE organizationId = ?` | `WHERE ownerId = ?` | `WHERE deletedAt IS NULL` | n/a |\n| `DELETE` | `WHERE organizationId = ?` | `WHERE ownerId = ?` | `WHERE deletedAt IS NULL` | n/a |\n\nScoping is duck-typed at runtime — tables with an `organizationId` column get org scoping, tables with `ownerId` get owner scoping, tables with `deletedAt` get soft delete filtering.\n\n```typescript\nexecute: async ({ db, ctx, input }) => {\n // This query automatically includes WHERE organizationId = ? AND deletedAt IS NULL\n const items = await db.select().from(applications).where(eq(applications.stage, 'interview'));\n\n // Inserts automatically include organizationId\n await db.insert(applications).values({ candidateId: input.candidateId, jobId: input.jobId, stage: 'applied' });\n\n return items;\n}\n```\n\n**Not enforced** in scoped DB (by design):\n- **Guards** — actions ARE the authorized way to modify guarded fields\n- **Masking** — actions are backend code that may need raw data\n- **Access** — already checked before action execution\n\n### Unsafe Mode\n\nActions that need to bypass scoped DB filters (for example, platform-level support operations) can enable unsafe mode.\n\nUse object form for explicit policy and mandatory audit metadata:\n\n```typescript\nexport default defineActions(applications, {\n adminReport: {\n description: \"Generate cross-org hiring report\",\n unsafe: {\n reason: \"Support investigation for enterprise customer\",\n adminOnly: true, // default true\n crossTenant: true, // default true\n targetScope: \"all\", // \"all\" | \"organization\"\n },\n input: z.object({ startDate: z.string() }),\n access: { roles: [\"owner\"] },\n execute: async ({ db, rawDb, ctx, input }) => {\n // db is still scoped (safety net)\n // rawDb bypasses scoped filters\n const allOrgs = await rawDb.select().from(applications);\n return allOrgs;\n },\n },\n});\n```\n\nUnsafe cross-tenant actions are generated with:\n\n- Better Auth authentication required (no unauthenticated path)\n- platform admin gate (`ctx.userRole === \"admin\"`)\n- mandatory audit logging on deny/success/error\n\nWithout unsafe mode, `rawDb` is `undefined` in the executor params.\n\nLegacy `unsafe: true` is still supported, but object mode is recommended so audit logs include an explicit reason.\n\n### Raw SQL Policy\n\nBy default, the compiler rejects raw SQL in action code and handler files. Use Drizzle query-builder syntax whenever possible.\n\nIf a specific action must use raw SQL, opt in explicitly:\n\n```typescript\nreconcileLedger: {\n description: \"Run custom reconciliation query\",\n allowRawSql: true,\n input: z.object({}),\n access: { roles: [\"owner\"] },\n execute: async ({ db }) => {\n // Allowed because allowRawSql: true\n return db.execute(sql`select 1`);\n },\n}\n```\n\nWithout `allowRawSql: true`, compilation fails with a loud error pointing to the action and snippet.\n\n## Handler Files\n\nFor complex actions, separate the logic into handler files.\n\n### When to Use Handler Files\n\n- Complex business logic spanning multiple operations\n- External API integrations\n- File generation or processing\n- Logic reused across multiple actions\n\n### Handler Structure\n\n```typescript\n// handlers/generate-report.ts\n\nexport const execute: ActionExecutor = async ({ db, ctx, input, services }) => {\n const apps = await db\n .select()\n .from(applicationsTable)\n .where(between(applicationsTable.createdAt, input.startDate, input.endDate));\n\n const pdf = await services.pdf.generate(apps);\n\n return {\n file: pdf,\n filename: `hiring-report-${input.startDate}-${input.endDate}.pdf`,\n contentType: 'application/pdf',\n };\n};\n```\n\n### Importing Tables\n\nHandler files can import tables from their own feature or other features. The compiler generates alias files for each table, so you import by the table's file name:\n\n```typescript\n// handlers/advance-stage.ts\n\n// Same feature — import from parent directory using the table's file name\n\n// Cross-feature — go up to the features directory, then into the other feature\n\nexport const execute: ActionExecutor = async ({ db, ctx, input, c }) => {\n const [job] = await db\n .select()\n .from(jobs)\n .where(eq(jobs.id, input.jobId))\n .limit(1);\n\n if (!job) {\n return c.json({ error: 'Job not found', code: 'NOT_FOUND' }, 404);\n }\n\n // ... continue with business logic\n};\n```\n\n**Path pattern from `features/{name}/handlers/`:**\n- Same feature table: `../{table-file-name}` (e.g., `../applications`)\n- Other feature table: `../../{other-feature}/{table-file-name}` (e.g., `../../candidates/candidates`)\n- Generated lib files: `../../../lib/{module}` (e.g., `../../../lib/realtime`, `../../../lib/webhooks`)\n\n### Executor Parameters\n\n```typescript\ninterface ActionExecutorParams {\n db: DrizzleDB; // Scoped database (auto-applies org/owner/soft-delete filters)\n rawDb?: DrizzleDB; // Raw database (only available when unsafe mode is enabled)\n ctx: AppContext; // User context (userId, roles, orgId)\n record?: TRecord; // The record (record-based only, undefined for standalone)\n input: TInput; // Validated input from Zod schema\n services: TServices; // Configured integrations (billing, notifications, etc.)\n c: HonoContext; // Raw Hono context for advanced use\n auditFields: object; // { createdAt, modifiedAt } timestamps\n}\n```\n\n## HTTP API Reference\n\n### Request Format\n\n| Method | Input Source | Use Case |\n|--------|--------------|----------|\n| `GET` | Query parameters | Read-only operations, fetching data |\n| `POST` | JSON body | Default, state-changing operations |\n| `PUT` | JSON body | Full replacement operations |\n| `PATCH` | JSON body | Partial updates |\n| `DELETE` | JSON body | Deletion with optional payload |\n\n```typescript\n// GET action - input comes from query params\ngetStageHistory: {\n method: \"GET\",\n input: z.object({ format: z.string().optional() }),\n // Called as: GET /applications/:id/getStageHistory?format=detailed\n}\n\n// POST action (default) - input comes from JSON body\n'advance-stage': {\n // method: \"POST\" is implied\n input: z.object({ stage: z.enum([\"screening\", \"interview\", \"offer\", \"hired\"]) }),\n // Called as: POST /applications/:id/advance-stage with JSON body\n}\n```\n\n### Response Formats\n\n| Type | Content-Type | Use Case |\n|------|--------------|----------|\n| `json` | `application/json` | Standard API responses (default) |\n| `stream` | `text/event-stream` | Real-time streaming (AI chat, live updates) |\n| `file` | Varies | File downloads (reports, exports) |\n\n### Error Codes\n\n| Status | Description |\n|--------|-------------|\n| `400` | Invalid input / validation error |\n| `401` | Not authenticated |\n| `403` | Access check failed (role or precondition) |\n| `404` | Record not found (record-based actions) |\n| `500` | Handler execution error |\n\n### Validation Error Response\n\n```json\n{\n \"error\": \"Invalid request data\",\n \"layer\": \"validation\",\n \"code\": \"VALIDATION_FAILED\",\n \"details\": {\n \"fields\": {\n \"amount\": \"Expected positive number\"\n }\n },\n \"hint\": \"Check the input schema for this action\"\n}\n```\n\n### Error Handling\n\n**Option 1: Return a JSON error response** (recommended for most cases)\n\nSince action handlers receive the Hono context (`c`), you can return error responses directly:\n\n```typescript\nexecute: async ({ ctx, record, input, c }) => {\n if (record.stage === 'hired') {\n return c.json({\n error: 'Cannot modify a hired application',\n code: 'ALREADY_HIRED',\n details: { currentStage: record.stage },\n }, 400);\n }\n // ... continue\n}\n```\n\n**Option 2: Throw an ActionError**\n\n```typescript\n\nexecute: async ({ ctx, record, input }) => {\n if (record.stage === 'hired') {\n throw new ActionError('Cannot modify a hired application', 'ALREADY_HIRED', 400, {\n currentStage: record.stage,\n });\n }\n // ... continue\n}\n```\n\nThe `ActionError` constructor signature is `(message, code, statusCode, details?)`.\n\n## Protected Fields\n\nActions can modify fields that are protected from regular CRUD operations:\n\n```typescript\n// In resource.ts\nguards: {\n protected: {\n stage: [\"advance-stage\", \"reject\"], // Only these actions can modify stage\n }\n}\n```\n\nThis allows the `advance-stage` action to set `stage = \"interview\"` even though the field is protected from regular PATCH requests.\n\n## Examples\n\n### Application Stage Advance (Record-Based)\n\n```typescript\nexport default defineActions(applications, {\n 'advance-stage': {\n description: \"Move application to the next pipeline stage\",\n input: z.object({\n stage: z.enum([\"screening\", \"interview\", \"offer\", \"hired\"]),\n notes: z.string().optional(),\n }),\n access: {\n roles: [\"owner\", \"hiring-manager\"],\n record: { stage: { notEquals: \"rejected\" } },\n },\n execute: async ({ db, ctx, record, input }) => {\n const [updated] = await db\n .update(applications)\n .set({\n stage: input.stage,\n notes: input.notes ?? record.notes,\n })\n .where(eq(applications.id, record.id))\n .returning();\n return updated;\n },\n },\n});\n```\n\n### AI Chat (Standalone with Streaming)\n\n```typescript\nexport default defineActions(sessions, {\n chat: {\n description: \"Send a message to AI assistant\",\n standalone: true,\n path: \"/chat\",\n responseType: \"stream\",\n input: z.object({\n message: z.string().min(1).max(2000),\n }),\n access: { roles: [\"recruiter\", \"hiring-manager\"] },\n handler: \"./handlers/chat\",\n },\n});\n```\n\n### Bulk Import (Standalone, No Record)\n\n```typescript\nexport default defineActions(candidates, {\n bulkImport: {\n description: \"Import candidates from job board CSV\",\n standalone: true,\n path: \"/candidates/import\",\n input: z.object({\n data: z.array(z.object({\n email: z.string().email(),\n name: z.string(),\n })),\n }),\n access: { roles: [\"hiring-manager\", \"recruiter\"] },\n execute: async ({ db, input }) => {\n const inserted = await db\n .insert(candidates)\n .values(input.data)\n .returning();\n return { imported: inserted.length };\n },\n },\n});\n```"
|
|
159
159
|
},
|
|
160
160
|
"compiler/definitions/concepts": {
|
|
161
161
|
"title": "Glossary",
|
|
@@ -171,7 +171,7 @@ export const DOCS = {
|
|
|
171
171
|
},
|
|
172
172
|
"compiler/definitions": {
|
|
173
173
|
"title": "Definitions Overview",
|
|
174
|
-
"content": "Before diving into specific features, let's understand how Quickback's pieces connect. This page gives you the mental model for everything that follows.\n\n## The Big Picture\n\nQuickback is a **backend compiler**. You write definition files, and Quickback compiles them into a production-ready API.\n\n1. **You write definitions** - Table files with schema and security config using `defineTable`\n2. **Quickback compiles them** - Analyzes your definitions at build time\n3. **You get a production API** - `GET /jobs`, `POST /jobs`, `PATCH /jobs/:id`, `DELETE /jobs/:id`, batch operations, plus custom actions\n\n## File Structure\n\nYour definitions live in a `quickback/features/` folder organized by feature:\n\n```\nmy-app/\n├── quickback/\n│ ├── quickback.config.ts # Compiler configuration\n│ └── features/\n│ └── {feature-name}/\n│ ├── candidates.ts # Table + config (defineTable)\n│ ├── applications.ts # Secondary table + config\n│ ├── interview-scores.ts # Internal table (no routes)\n│ ├── actions.ts # Custom actions (optional)\n│ └── handlers/ # Action handlers (optional)\n│ └── advance-stage.ts\n├── src/ # Generated code (output)\n
|
|
174
|
+
"content": "Before diving into specific features, let's understand how Quickback's pieces connect. This page gives you the mental model for everything that follows.\n\n## The Big Picture\n\nQuickback is a **backend compiler**. You write definition files, and Quickback compiles them into a production-ready API.\n\n1. **You write definitions** - Table files with schema and security config using `defineTable`\n2. **Quickback compiles them** - Analyzes your definitions at build time\n3. **You get a production API** - `GET /jobs`, `POST /jobs`, `PATCH /jobs/:id`, `DELETE /jobs/:id`, batch operations, plus custom actions\n\n## File Structure\n\nYour definitions live in a `quickback/features/` folder organized by feature:\n\n```\nmy-app/\n├── quickback/\n│ ├── quickback.config.ts # Compiler configuration\n│ └── features/\n│ └── {feature-name}/\n│ ├── candidates.ts # Table + config (defineTable)\n│ ├── applications.ts # Secondary table + config\n│ ├── interview-scores.ts # Internal table (no routes)\n│ ├── actions.ts # Custom actions (optional)\n│ └── handlers/ # Action handlers (optional)\n│ └── advance-stage.ts\n├── src/ # Generated code (output)\n└── package.json\n```\n\n**Table files** use `defineTable` to combine schema and security config:\n\n```typescript\n// features/candidates/candidates.ts\n\nexport const candidates = sqliteTable(\"candidates\", {\n id: text(\"id\").primaryKey(),\n organizationId: text(\"organization_id\").notNull(),\n name: text(\"name\").notNull(),\n email: text(\"email\").notNull(),\n phone: text(\"phone\"),\n source: text(\"source\"),\n});\n\nexport default defineTable(candidates, {\n firewall: { organization: {} },\n guards: {\n createable: [\"name\", \"email\", \"phone\", \"source\"],\n updatable: [\"name\", \"phone\"],\n },\n masking: {\n email: { type: 'email', show: { roles: ['owner', 'hiring-manager', 'recruiter'] } },\n phone: { type: 'phone', show: { roles: ['owner', 'hiring-manager', 'recruiter'] } },\n },\n crud: {\n list: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\", \"interviewer\"] } },\n get: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\", \"interviewer\"] } },\n create: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\"] } },\n update: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\"] } },\n delete: { access: { roles: [\"owner\", \"hiring-manager\"] } },\n },\n});\n\nexport type Candidate = typeof candidates.$inferSelect;\n```\n\n**Key points:**\n- Tables with `export default defineTable(...)` → CRUD routes generated\n- Tables without default export → internal/junction tables (no routes)\n- Route path derived from filename: `applications.ts` → `/api/v1/applications`\n\n## The Four Security Layers\n\nEvery API request passes through four security layers, in order:\n\n```\nRequest → Firewall → Access → Guards → Masking → Response\n │ │ │ │\n │ │ │ └── Hide sensitive fields\n │ │ └── Block field modifications\n │ └── Check roles & conditions\n └── Isolate data by owner/org/team\n```\n\n### 1. Firewall (Data Isolation)\n\nThe firewall controls **which records** a user can see. It automatically adds WHERE clauses to every query based on your schema columns:\n\n| Column in Schema | What happens |\n|------------------|--------------|\n| `organization_id` | Data isolated by organization |\n| `user_id` | Data isolated by user (personal data) |\n\nNo manual configuration needed - Quickback applies smart rules based on your schema.\n\n[Learn more about Firewall →](/compiler/definitions/firewall)\n\n### 2. Access (CRUD Permissions)\n\nAccess controls **which operations** a user can perform. It checks roles and record conditions.\n\n```typescript\ncrud: {\n list: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\", \"interviewer\"] } },\n create: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\"] } },\n update: { access: { roles: [\"owner\", \"hiring-manager\", \"recruiter\"] } },\n delete: { access: { roles: [\"owner\", \"hiring-manager\"] } },\n}\n```\n\n[Learn more about Access →](/compiler/definitions/access)\n\n### 3. Guards (Field Modification Rules)\n\nGuards control **which fields** can be modified in each operation.\n\n| Guard Type | What it means |\n|------------|---------------|\n| `createable` | Fields that can be set when creating |\n| `updatable` | Fields that can be changed when updating |\n| `protected` | Fields that can only be changed via specific actions |\n| `immutable` | Fields that can never be changed after creation |\n\n[Learn more about Guards →](/compiler/definitions/guards)\n\n### 4. Masking (Data Redaction)\n\nMasking hides sensitive fields from users who shouldn't see them.\n\n```typescript\nmasking: {\n email: { type: 'email' }, // Shows: j***@e******.com\n phone: { type: 'phone' }, // Shows: ******4567\n salary: { type: 'redact' }, // Shows: [REDACTED]\n}\n```\n\n[Learn more about Masking →](/compiler/definitions/masking)\n\n## How They Work Together\n\n**Scenario:** An interviewer requests `GET /candidates/cnd_123`\n\n1. **Firewall** checks: Is candidate `cnd_123` in the user's organization?\n - Yes → Continue\n - No → 404 Not Found (as if it doesn't exist)\n\n2. **Access** checks: Can interviewers perform GET?\n - Yes → Continue\n - No → 403 Forbidden\n\n3. **Guards** don't apply to GET (they're for writes)\n\n4. **Masking** applies: User is an `interviewer`, not `recruiter` or `hiring-manager`\n - Email: `jane@company.com` → `j***@c******.com`\n - Phone: `555-123-4567` → `******4567`\n\n5. **Response** sent with masked data\n\n## Locked Down by Default\n\nQuickback is **secure by default**. Nothing is accessible until you explicitly allow it.\n\n| Layer | Default | What you must do |\n|-------|---------|------------------|\n| Firewall | AUTO | Auto-detects from `organization_id`/`user_id` columns. Only configure for exceptions. |\n| Access | DENIED | Explicitly define `access` rules with roles |\n| Guards | LOCKED | Explicitly list `createable`, `updatable` fields |\n| Actions | BLOCKED | Explicitly define `access` for each action |\n\n**You must deliberately open each door.** This prevents accidental data exposure.\n\n## Next Steps\n\n1. [Database Schema](/compiler/definitions/schema) — Define your tables\n2. [Firewall](/compiler/definitions/firewall) — Set up data isolation\n3. [Access](/compiler/definitions/access) — Configure CRUD permissions\n4. [Guards](/compiler/definitions/guards) — Control field modifications\n5. [Masking](/compiler/definitions/masking) — Hide sensitive data\n6. [Views](/compiler/definitions/views) — Column-level security\n7. [Validation](/compiler/definitions/validation) — Field validation rules\n8. [Actions](/compiler/definitions/actions) — Add custom business logic"
|
|
175
175
|
},
|
|
176
176
|
"compiler/definitions/masking": {
|
|
177
177
|
"title": "Masking - Field Redaction",
|
|
@@ -211,11 +211,11 @@ export const DOCS = {
|
|
|
211
211
|
},
|
|
212
212
|
"compiler/getting-started/template-bun": {
|
|
213
213
|
"title": "Bun Template",
|
|
214
|
-
"content": "The Bun template creates a backend running on Bun with a local SQLite database and Better Auth. No cloud account needed — ideal for local development and prototyping.\n\n## Create the Project\n\n```bash\nquickback create bun my-app\ncd my-app\n```\n\nAliases: `quickback create local my-app`\n\nThis scaffolds a project with:\n\n```\nmy-app/\n├── quickback/\n│ ├── quickback.config.ts # Compiler configuration\n│ └── features/\n│ └── todos/\n│ ├── todos.ts # Schema + security (defineTable)\n│ └── actions.ts # Custom actions\n├── src/ # Compiled output (generated)\n├──
|
|
214
|
+
"content": "The Bun template creates a backend running on Bun with a local SQLite database and Better Auth. No cloud account needed — ideal for local development and prototyping.\n\n## Create the Project\n\n```bash\nquickback create bun my-app\ncd my-app\n```\n\nAliases: `quickback create local my-app`\n\nThis scaffolds a project with:\n\n```\nmy-app/\n├── quickback/\n│ ├── quickback.config.ts # Compiler configuration\n│ └── features/\n│ └── todos/\n│ ├── todos.ts # Schema + security (defineTable)\n│ └── actions.ts # Custom actions\n├── src/ # Compiled output (generated)\n├── data/ # SQLite database files\n├── package.json\n├── tsconfig.json\n└── drizzle.config.ts\n```\n\n## Generated Configuration\n\n### quickback.config.ts\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n template: \"hono\",\n features: [\"organizations\"],\n providers: {\n runtime: { name: \"bun\", config: {} },\n database: {\n name: \"better-sqlite3\",\n config: { path: \"./data/app.db\" },\n },\n auth: { name: \"better-auth\", config: {} },\n },\n});\n```\n\nThe Bun template includes the same `todos` feature as the [Cloudflare template](/compiler/getting-started/template-cloudflare) with all four security pillars configured.\n\n## Setup Steps\n\n### 1. Install Dependencies\n\n```bash\nbun install\n```\n\n### 2. Log In to the Compiler\n\n```bash\nquickback login\n```\n\n### 3. Compile Your Definitions\n\n```bash\nquickback compile\n```\n\n### 4. Run Migrations\n\n```bash\nnpm run db:migrate\n```\n\nThis creates the SQLite database in `data/app.db` and applies all migrations.\n\n### 5. Create Environment File\n\nCreate a `.env` file in your project root:\n\n```bash\n# Runtime\nNODE_ENV=development\nPORT=3000\n\n# Auth\nBETTER_AUTH_SECRET=your-secret-key-change-in-production\nBETTER_AUTH_URL=http://localhost:3000\n```\n\n**Note:** Generate a strong random string for `BETTER_AUTH_SECRET` in production. You can use `openssl rand -hex 32`.\n\n### 6. Start Development Server\n\n```bash\nnpm run dev\n```\n\nYour API is running at `http://localhost:3000` with hot reload.\n\n## Database\n\nThe Bun template uses `better-sqlite3` with a local SQLite file stored in `data/app.db`. The generated database module automatically:\n\n- Creates the `data/` directory if it doesn't exist\n- Enables WAL mode for better concurrent performance\n- Enables foreign key constraints\n\n```\ndata/\n└── app.db # SQLite database file\n```\n\nUnlike the Cloudflare template, Bun uses a **single database** for both auth and feature tables. Migrations are in a single directory:\n\n```\nquickback/drizzle/\n├── meta/\n│ ├── _journal.json\n│ └── 0000_snapshot.json\n└── 0000_initial.sql\n```\n\n## Available Scripts\n\n| Script | Command | Description |\n|--------|---------|-------------|\n| `dev` | `bun run --hot src/index.ts` | Start dev server with hot reload |\n| `start` | `bun run src/index.ts` | Start production server |\n| `build` | `bun build src/index.ts --outdir ./dist --target bun` | Build for production |\n| `db:migrate` | `npx drizzle-kit migrate` | Apply database migrations |\n| `type-check` | `tsc --noEmit` | Run TypeScript type checking |\n\n## Switching to Cloudflare\n\nWhen you're ready to deploy to production, you can switch to the Cloudflare template by updating your `quickback.config.ts`:\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n template: \"hono\",\n features: [\"organizations\"],\n providers: {\n runtime: { name: \"cloudflare\", config: {} },\n database: {\n name: \"cloudflare-d1\",\n config: { binding: \"DB\" },\n },\n auth: { name: \"better-auth\", config: {} },\n },\n});\n```\n\nThen recompile:\n\n```bash\nquickback compile\n```\n\nThe compiler regenerates the entire `src/` directory for the new runtime target. Your feature definitions stay the same.\n\n## Next Steps\n\n- [Add a new feature](/compiler/definitions/schema) — Define your own tables with security\n- [Configure access control](/compiler/definitions/access) — Set role-based permissions\n- [Add custom actions](/compiler/definitions/actions) — Business logic beyond CRUD\n- [Cloudflare template](/compiler/getting-started/template-cloudflare) — Deploy to production"
|
|
215
215
|
},
|
|
216
216
|
"compiler/getting-started/template-cloudflare": {
|
|
217
217
|
"title": "Cloudflare Template",
|
|
218
|
-
"content": "The Cloudflare template creates a production-ready backend running on Cloudflare Workers with D1 (SQLite at the edge), KV storage, and Better Auth.\n\n## Create the Project\n\n```bash\nquickback create cloudflare my-app\ncd my-app\n```\n\nAliases: `quickback create cf my-app`\n\nThis scaffolds a project with:\n\n```\nmy-app/\n├── quickback/\n│ ├── quickback.config.ts # Compiler configuration\n│ └── features/\n│ └── todos/\n│ ├── todos.ts # Schema + security (defineTable)\n│ └── actions.ts # Custom actions\n├── src/ # Compiled output (generated)\n├──
|
|
218
|
+
"content": "The Cloudflare template creates a production-ready backend running on Cloudflare Workers with D1 (SQLite at the edge), KV storage, and Better Auth.\n\n## Create the Project\n\n```bash\nquickback create cloudflare my-app\ncd my-app\n```\n\nAliases: `quickback create cf my-app`\n\nThis scaffolds a project with:\n\n```\nmy-app/\n├── quickback/\n│ ├── quickback.config.ts # Compiler configuration\n│ └── features/\n│ └── todos/\n│ ├── todos.ts # Schema + security (defineTable)\n│ └── actions.ts # Custom actions\n├── src/ # Compiled output (generated)\n├── package.json\n├── tsconfig.json\n├── wrangler.toml\n└── drizzle.config.ts\n```\n\n## Generated Configuration\n\n### quickback.config.ts\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n template: \"hono\",\n features: [\"organizations\"],\n providers: {\n runtime: { name: \"cloudflare\", config: {} },\n database: {\n name: \"cloudflare-d1\",\n config: { binding: \"DB\" },\n },\n auth: { name: \"better-auth\", config: {} },\n },\n});\n```\n\n### Example Feature (todos.ts)\n\nThe template includes a `todos` feature with all four security pillars configured:\n\n```typescript\n\nexport const todos = sqliteTable(\"todos\", {\n id: text(\"id\").primaryKey(),\n title: text(\"title\").notNull(),\n description: text(\"description\"),\n completed: integer(\"completed\", { mode: \"boolean\" }).default(false),\n priority: text(\"priority\").default(\"medium\"),\n dueDate: integer(\"due_date\", { mode: \"timestamp\" }),\n organizationId: text(\"organization_id\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n});\n\nexport default defineTable(todos, {\n firewall: {\n organization: {},\n owner: { mode: \"optional\" },\n softDelete: {},\n },\n guards: {\n createable: [\"title\", \"description\", \"completed\", \"priority\", \"dueDate\"],\n updatable: [\"title\", \"description\", \"completed\", \"priority\", \"dueDate\"],\n immutable: [\"id\", \"organizationId\", \"ownerId\"],\n },\n masking: {\n description: {\n type: \"redact\",\n show: { roles: [\"owner\", \"admin\"] },\n },\n },\n crud: {\n list: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n get: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n create: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n update: { access: { roles: [\"owner\", \"admin\", \"member\"] } },\n delete: { access: { roles: [\"owner\", \"admin\"] }, mode: \"soft\" },\n },\n});\n```\n\n## Setup Steps\n\n### 1. Install Dependencies\n\n```bash\nnpm install\n```\n\n### 2. Log In to the Compiler\n\n```bash\nquickback login\n```\n\n### 3. Compile Your Definitions\n\n```bash\nquickback compile\n```\n\nThis sends your definitions to the Quickback compiler and generates the full `src/` directory, migrations, and configuration files.\n\n### 4. Create D1 Databases\n\nThe Cloudflare template uses **dual database mode** by default — separate databases for auth and application data.\n\n```bash\n# Create the auth database\nnpx wrangler d1 create my-app-auth\n\n# Create the features database\nnpx wrangler d1 create my-app-features\n```\n\nCopy the database IDs from the output and update your `wrangler.toml`:\n\n```toml\n[[d1_databases]]\nbinding = \"AUTH_DB\"\ndatabase_name = \"my-app-auth\"\ndatabase_id = \"paste-auth-id-here\"\n\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"my-app-features\"\ndatabase_id = \"paste-features-id-here\"\n```\n\n### 5. Run Migrations (Local)\n\n```bash\nnpm run db:migrate:local\n```\n\nThis applies migrations to both databases locally.\n\n### 6. Start Development Server\n\n```bash\nnpm run dev\n```\n\nYour API is running at `http://localhost:8787`.\n\n## Wrangler Configuration\n\nThe compiler generates a `wrangler.toml` with all required bindings:\n\n```toml\nname = \"my-app\"\nmain = \"src/index.ts\"\ncompatibility_date = \"2025-03-01\"\ncompatibility_flags = [\"nodejs_compat\"]\n\n[observability]\nenabled = true\n\n[placement]\nmode = \"smart\"\n\n# Auth database\n[[d1_databases]]\nbinding = \"AUTH_DB\"\ndatabase_name = \"my-app-auth\"\ndatabase_id = \"your-auth-db-id\"\n\n# Features database\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"my-app-features\"\ndatabase_id = \"your-features-db-id\"\n\n# Key-value storage\n[[kv_namespaces]]\nbinding = \"KV\"\nid = \"your-kv-id\"\n```\n\n### Optional Bindings\n\nDepending on your configuration, the compiler may also generate bindings for:\n\n| Binding | Type | When Generated |\n|---------|------|----------------|\n| `R2_BUCKET` | R2 Bucket | `fileStorage` configured |\n| `AI` | Workers AI | `embeddings` configured on any feature |\n| `VECTORIZE` | Vectorize Index | `embeddings` configured |\n| `EMBEDDINGS_QUEUE` | Queue | `embeddings` configured |\n| `WEBHOOKS_DB` | D1 | Webhooks enabled |\n| `WEBHOOKS_QUEUE` | Queue | Webhooks enabled |\n| `BROADCASTER` | Service | `realtime` configured |\n\n## Dual Database Mode\n\nBy default, the Cloudflare template separates auth tables from application tables:\n\n- **`AUTH_DB`** — Better Auth tables (`users`, `sessions`, `accounts`, `organizations`, `members`)\n- **`DB`** — Your feature tables (`todos`, etc.)\n\nThis keeps auth data isolated and allows independent scaling. Each database gets its own migration directory:\n\n```\nquickback/drizzle/\n├── auth/ # Auth migrations\n│ ├── meta/\n│ └── 0000_*.sql\n└── features/ # Feature migrations\n ├── meta/\n └── 0000_*.sql\n```\n\n## Deploying to Production\n\n### 1. Apply Remote Migrations\n\n```bash\nnpm run db:migrate:remote\n```\n\n### 2. Deploy to Cloudflare\n\n```bash\nnpm run deploy\n```\n\nThis runs `wrangler deploy` to push your worker to Cloudflare's edge network.\n\n## Available Scripts\n\n| Script | Command | Description |\n|--------|---------|-------------|\n| `dev` | `wrangler dev` | Start local dev server |\n| `deploy` | `npm run db:migrate:remote && wrangler deploy` | Deploy to production |\n| `db:migrate:local` | Runs auth + features migrations locally | Apply migrations to local D1 |\n| `db:migrate:remote` | Runs auth + features migrations remotely | Apply migrations to production D1 |\n\n## Next Steps\n\n- [Add a new feature](/compiler/definitions/schema) — Define your own tables with security\n- [Configure access control](/compiler/definitions/access) — Set role-based permissions\n- [Add custom actions](/compiler/definitions/actions) — Business logic beyond CRUD\n- [Environment variables](/compiler/config/variables) — Configure secrets and bindings"
|
|
219
219
|
},
|
|
220
220
|
"compiler/getting-started/templates": {
|
|
221
221
|
"title": "Templates",
|
|
@@ -223,11 +223,11 @@ export const DOCS = {
|
|
|
223
223
|
},
|
|
224
224
|
"compiler": {
|
|
225
225
|
"title": "Quickback Compiler",
|
|
226
|
-
"content": "The Quickback compiler transforms your declarative resource definitions into a complete, production-ready API. It analyzes your TypeScript definitions at build time, generates optimized code, validates your security configuration, and creates database migrations.\n\n## What the Compiler Does\n\nWhen you run `quickback compile`, the compiler:\n\n1. **Reads your definitions** - Analyzes all `defineTable()` configurations in your `quickback/features/` folder\n2. **Validates security** - Checks that firewall, guards, access, and masking are properly configured\n3. **Generates API routes** - Creates REST endpoints for each resource (GET, POST, PATCH, DELETE, plus batch operations)\n4. **Generates actions** - Creates custom endpoints from your `defineActions()` definitions\n5. **Creates middleware** - Generates authentication, authorization, and data validation logic\n6. **Generates TypeScript types** - Creates type-safe interfaces for your API\n7. **Generates migrations** - Automatically runs `drizzle-kit generate` to create database migration files\n\n**Input:**\n```\nquickback/\n├── quickback.config.ts # Compiler configuration\n└── features/\n └── jobs/\n ├── jobs.ts # defineTable(...)\n └── actions.ts # defineActions(...)\n```\n\n**Output:**\n```\nsrc/\n├── routes/\n│ └── jobs.ts # Generated API handlers\n├── middleware/\n│ ├── auth.ts # Authentication logic\n│ └── firewall.ts # Data isolation queries\n└── types/\n └── jobs.ts # TypeScript interfaces\n```\n\n## Basic Usage\n\n### Compile Your Project\n\n```bash\nquickback compile\n```\n\nThis command:\n- Reads your `quickback.config.ts`\n- Analyzes all resource definitions\n- Generates the complete API codebase\n- Reports any configuration errors\n\n### After Compilation\n\nOnce compilation succeeds, you need to:\n\n1. **Apply migrations:**\n\n Run the database migration for your provider. The generated `package.json` includes the appropriate migration script for your setup.\n\n > **Note:** The compiler automatically runs `drizzle-kit generate` during compilation, so you only need to apply the migrations.\n\n2. **Deploy your API:**\n ```bash\n npm run deploy # Cloudflare Workers\n # or\n npm start # Local development\n ```\n\n## The Compilation Process\n\n### 1. Configuration Loading\n\nThe compiler reads your `quickback.config.ts`:\n\n```typescript\n\nexport default defineConfig({\n name: 'my-app',\n template: 'hono',\n features: ['organizations'],\n providers: {\n runtime: defineRuntime('cloudflare'),\n database: defineDatabase('cloudflare-d1'),\n auth: defineAuth('better-auth'),\n },\n});\n```\n\nThis determines:\n- Which application template to use (`hono` or experimental `nextjs`)\n- Which database adapter to use (D1, SQLite, Turso, etc.)\n- Which authentication system to integrate\n- Which runtime to target (Cloudflare Workers, Bun, Node.js)\n- Which auth plugins to enable (based on `features`)\n\n### 2. Resource Discovery\n\nThe compiler scans `quickback/features/` and identifies:\n\n**Resources (with routes):**\n```typescript\n// quickback/features/jobs/jobs.ts\nexport default defineTable(jobs, { ... });\n// → Generates: GET/POST/PATCH/DELETE /api/v1/jobs\n// → Also generates: POST/PATCH/DELETE/PUT /api/v1/jobs/batch (auto-enabled)\n```\n\n**Internal tables (no routes):**\n```typescript\n// quickback/features/jobs/job-applications.ts\nexport const jobApplications = sqliteTable(...);\n// NO default export → No routes generated\n```\n\n**Actions:**\n```typescript\n// quickback/features/jobs/actions.ts\nexport default defineActions(jobs, {\n publish: { ... },\n close: { ... }\n});\n// → Generates: POST /api/v1/jobs/:id/publish, POST /api/v1/jobs/:id/close\n```\n\n### 3. Validation\n\nThe compiler validates your configuration at build time, catching errors before deployment. See [Definitions](/compiler/definitions) for details on what's validated.\n\n### 4. Code Generation\n\nThe compiler generates different files based on your provider:\n\n#### Cloudflare Workers (Hono)\n\n```typescript\n// Generated: src/routes/jobs.ts\n\nconst app = new Hono();\n\napp.get('/jobs', async (c) => {\n const ctx = c.get('ctx');\n let query = db.select().from(jobs);\n query = applyFirewall(query, ctx, 'jobs');\n await checkAccess(ctx, 'jobs', 'list');\n const results = await query;\n return c.json(results);\n});\n```\n\n### 5. Type Generation\n\nThe compiler generates TypeScript types for your API:\n\n```typescript\n// Generated: src/types/jobs.ts\nexport type Job = typeof jobs.$inferSelect;\nexport type JobInsert = typeof jobs.$inferInsert;\n```\n\n## Build-Time vs Runtime\n\n### Build Time (Compilation)\n\nThe compiler validates security configuration, checks for schema/firewall mismatches, generates API route handlers, creates TypeScript types, and prepares migration setup.\n\n### Runtime (API Requests)\n\nWhen your API receives a request: Authentication → Firewall → Access → Guards → Masking → Response. The compiled code handles all of this automatically.\n\n## Development Workflow\n\n1. **Define resources** in `quickback/features/`\n2. **Compile** with `quickback compile`\n3. **Review migrations** in `drizzle/`\n4. **Apply migrations** for your provider\n5. **Test locally** with `npm run dev`\n6. **Deploy** with `npm run deploy`\n\n## Next Steps\n\n- [Getting Started](/compiler/getting-started) — Create your first project\n- [Definitions](/compiler/definitions) — Schema, security layers, and actions\n- [Using the API](/compiler/using-the-api) — CRUD endpoints and filtering\n- [Cloud Compiler](/compiler/cloud-compiler) — CLI and authentication"
|
|
226
|
+
"content": "The Quickback compiler transforms your declarative resource definitions into a complete, production-ready API. It analyzes your TypeScript definitions at build time, generates optimized code, validates your security configuration, and creates database migrations.\n\n## What the Compiler Does\n\nWhen you run `quickback compile`, the compiler:\n\n1. **Reads your definitions** - Analyzes all `defineTable()` configurations in your `quickback/features/` folder\n2. **Validates security** - Checks that firewall, guards, access, and masking are properly configured\n3. **Generates API routes** - Creates REST endpoints for each resource (GET, POST, PATCH, DELETE, plus batch operations)\n4. **Generates actions** - Creates custom endpoints from your `defineActions()` definitions\n5. **Creates middleware** - Generates authentication, authorization, and data validation logic\n6. **Generates TypeScript types** - Creates type-safe interfaces for your API\n7. **Generates migrations** - Automatically runs `drizzle-kit generate` to create database migration files\n\n**Input:**\n```\nquickback/\n├── quickback.config.ts # Compiler configuration\n└── features/\n └── jobs/\n ├── jobs.ts # defineTable(...)\n └── actions.ts # defineActions(...)\n```\n\n**Output:**\n```\nsrc/\n├── routes/\n│ └── jobs.ts # Generated API handlers\n├── middleware/\n│ ├── auth.ts # Authentication logic\n│ └── firewall.ts # Data isolation queries\n└── types/\n └── jobs.ts # TypeScript interfaces\n```\n\n## Basic Usage\n\n### Compile Your Project\n\n```bash\nquickback compile\n```\n\nThis command:\n- Reads your `quickback.config.ts`\n- Analyzes all resource definitions\n- Generates the complete API codebase\n- Reports any configuration errors\n\n### After Compilation\n\nOnce compilation succeeds, you need to:\n\n1. **Apply migrations:**\n\n Run the database migration for your provider. The generated `package.json` includes the appropriate migration script for your setup.\n\n > **Note:** The compiler automatically runs `drizzle-kit generate` during compilation, so you only need to apply the migrations.\n\n2. **Deploy your API:**\n ```bash\n npm run deploy # Cloudflare Workers\n # or\n npm start # Local development\n ```\n\n## The Compilation Process\n\n### 1. Configuration Loading\n\nThe compiler reads your `quickback.config.ts`:\n\n```typescript\n\nexport default defineConfig({\n name: 'my-app',\n template: 'hono',\n features: ['organizations'],\n providers: {\n runtime: defineRuntime('cloudflare'),\n database: defineDatabase('cloudflare-d1'),\n auth: defineAuth('better-auth'),\n },\n});\n```\n\nThis determines:\n- Which application template to use (`hono` or experimental `nextjs`)\n- Which database adapter to use (D1, SQLite, Turso, etc.)\n- Which authentication system to integrate\n- Which runtime to target (Cloudflare Workers, Bun, Node.js)\n- Which auth plugins to enable (based on `features`)\n\n### 2. Resource Discovery\n\nThe compiler scans `quickback/features/` and identifies:\n\n**Resources (with routes):**\n```typescript\n// quickback/features/jobs/jobs.ts\nexport default defineTable(jobs, { ... });\n// → Generates: GET/POST/PATCH/DELETE /api/v1/jobs\n// → Also generates: POST/PATCH/DELETE/PUT /api/v1/jobs/batch (auto-enabled)\n```\n\n**Internal tables (no routes):**\n```typescript\n// quickback/features/jobs/job-applications.ts\nexport const jobApplications = sqliteTable(...);\n// NO default export → No routes generated\n```\n\n**Actions:**\n```typescript\n// quickback/features/jobs/actions.ts\nexport default defineActions(jobs, {\n publish: { ... },\n close: { ... }\n});\n// → Generates: POST /api/v1/jobs/:id/publish, POST /api/v1/jobs/:id/close\n```\n\n### 3. Validation\n\nThe compiler validates your configuration at build time, catching errors before deployment. See [Definitions](/compiler/definitions) for details on what's validated.\n\n### 4. Code Generation\n\nThe compiler generates different files based on your provider:\n\n#### Cloudflare Workers (Hono)\n\n```typescript\n// Generated: src/routes/jobs.ts\n\nconst app = new Hono();\n\napp.get('/jobs', async (c) => {\n const ctx = c.get('ctx');\n let query = db.select().from(jobs);\n query = applyFirewall(query, ctx, 'jobs');\n await checkAccess(ctx, 'jobs', 'list');\n const results = await query;\n return c.json(results);\n});\n```\n\n### 5. Type Generation\n\nThe compiler generates TypeScript types for your API:\n\n```typescript\n// Generated: src/types/jobs.ts\nexport type Job = typeof jobs.$inferSelect;\nexport type JobInsert = typeof jobs.$inferInsert;\n```\n\n## Build-Time vs Runtime\n\n### Build Time (Compilation)\n\nThe compiler validates security configuration, checks for schema/firewall mismatches, generates API route handlers, creates TypeScript types, and prepares migration setup.\n\n### Runtime (API Requests)\n\nWhen your API receives a request: Authentication → Firewall → Access → Guards → Masking → Response. The compiled code handles all of this automatically.\n\n## Development Workflow\n\n1. **Define resources** in `quickback/features/`\n2. **Compile** with `quickback compile`\n3. **Review migrations** in `quickback/drizzle/`\n4. **Apply migrations** for your provider\n5. **Test locally** with `npm run dev`\n6. **Deploy** with `npm run deploy`\n\n## Next Steps\n\n- [Getting Started](/compiler/getting-started) — Create your first project\n- [Definitions](/compiler/definitions) — Schema, security layers, and actions\n- [Using the API](/compiler/using-the-api) — CRUD endpoints and filtering\n- [Cloud Compiler](/compiler/cloud-compiler) — CLI and authentication"
|
|
227
227
|
},
|
|
228
228
|
"compiler/integrations/cloudflare": {
|
|
229
229
|
"title": "Cloudflare Workers",
|
|
230
|
-
"content": "The Cloudflare target generates a complete Hono-based API running on Cloudflare Workers with D1 as the database. This is the recommended target for production deployments.\n\n## Configuration\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n template: \"hono\",\n features: [\"organizations\"],\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\"),\n auth: defineAuth(\"better-auth\"),\n },\n});\n```\n\n## Generated Output\n\n```\nsrc/\n├── routes/ # Hono route handlers (one per resource)\n├── middleware/ # Auth, firewall, access middleware\n├── features/ # Feature-specific helpers (security, masking)\n├── types/ # TypeScript interfaces\n├── db/\n│ └── schema.ts # Drizzle schema\n└── index.ts # Hono app entry point\n\
|
|
230
|
+
"content": "The Cloudflare target generates a complete Hono-based API running on Cloudflare Workers with D1 as the database. This is the recommended target for production deployments.\n\n## Configuration\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n template: \"hono\",\n features: [\"organizations\"],\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\"),\n auth: defineAuth(\"better-auth\"),\n },\n});\n```\n\n## Generated Output\n\n```\nsrc/\n├── routes/ # Hono route handlers (one per resource)\n├── middleware/ # Auth, firewall, access middleware\n├── features/ # Feature-specific helpers (security, masking)\n├── types/ # TypeScript interfaces\n├── db/\n│ └── schema.ts # Drizzle schema\n└── index.ts # Hono app entry point\n\nquickback/drizzle/\n├── migrations/ # SQL migration files\n└── meta/ # Drizzle metadata\n```\n\n## Security Model\n\nAll four security layers run at the application level:\n\n1. **Firewall** — Drizzle WHERE clauses for data isolation\n2. **Access** — Role checks in middleware\n3. **Guards** — Field filtering in request handlers\n4. **Masking** — Response transformation before sending\n\nSince D1 is only accessible through your Worker (no external connection string), the application layer is the only entry point. See [D1 Security Architecture](/stack/database/d1#security-architecture) for details.\n\n## Features\n\n- Full CRUD with batch operations (create, update, delete, upsert)\n- Custom actions with inline or handler-based execution\n- Soft delete with cascading to child tables\n- Pagination, filtering, sorting, field selection, and full-text search\n- Views (column-level projections with per-view access control)\n- OpenAPI specification generation at `/openapi.json`\n\n## Deployment\n\n```bash\n# Local development\nnpm run dev\n\n# Apply migrations to remote D1\nnpm run db:migrate:remote\n\n# Deploy to Cloudflare Workers\nnpm run deploy\n```\n\n## Environment & Bindings\n\nYour `wrangler.toml` needs these bindings:\n\n| Binding | Type | Required | Purpose |\n|---------|------|----------|---------|\n| `DB` | D1 | Yes | Feature database |\n| `AUTH_DB` | D1 | Yes | Better Auth database |\n| `AUDIT_DB` | D1 | No* | Unsafe cross-tenant action audit logs |\n| `KV` | KV | Yes | Session storage |\n| `R2` | R2 | No | File storage (avatars, uploads) |\n| `AI` | Workers AI | No | Embeddings generation |\n| `VECTORIZE` | Vectorize | No | Vector search index |\n| `BROADCASTER` | Service | No | Realtime broadcasts |\n\n\\* Required when you define cross-tenant unsafe actions.\n\n## See Also\n\n- [Cloudflare Template](/compiler/getting-started/template-cloudflare) — Step-by-step setup guide with full wrangler.toml configuration\n- [D1 Database](/stack/database/d1) — D1 setup, multi-database pattern, and security architecture\n- [Neon Integration](/compiler/integrations/neon) — Alternative: PostgreSQL with RLS\n- [Supabase Integration](/compiler/integrations/supabase) — Alternative: Supabase with RLS"
|
|
231
231
|
},
|
|
232
232
|
"compiler/integrations": {
|
|
233
233
|
"title": "Compile Targets",
|
|
@@ -235,7 +235,7 @@ export const DOCS = {
|
|
|
235
235
|
},
|
|
236
236
|
"compiler/integrations/neon": {
|
|
237
237
|
"title": "Neon",
|
|
238
|
-
"content": "Quickback supports Neon as a PostgreSQL database provider, generating schemas with Row Level Security (RLS) policies that enforce your firewall configuration at the database level.\n\n## Why Neon?\n\n- **Full PostgreSQL** — Advanced queries, joins, PostGIS, full-text search\n- **Database-Level Security** — RLS policies enforce access even if API is bypassed\n- **Serverless Architecture** — HTTP connection mode for Cloudflare Workers\n- **Neon Authorize** — JWT-based RLS with `auth.user_id()` function\n- **Better Auth Integration** — Works seamlessly with Better Auth\n\n## Configuration\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"neon\", {\n connectionMode: 'auto', // auto-detects based on runtime\n pooled: true,\n }),\n auth: defineAuth(\"better-auth\"),\n },\n});\n```\n\n### Environment Variables\n\n```bash\nDATABASE_URL=postgresql://user:pwd@ep-xxx.neon.tech/db?sslmode=require\n```\n\n## Row Level Security\n\nQuickback generates RLS policies from your firewall config:\n\n```typescript\nfirewall: {\n organization: {},\n owner: { mode: 'optional' }\n}\n```\n\n```sql\n-- Generated policy (uses auth.user_id() for Neon)\nCREATE POLICY \"documents_select\" ON documents FOR SELECT\nUSING (\n organization_id = get_active_org_id()\n AND (has_any_role(ARRAY['admin']) OR user_id = auth.user_id())\n);\n```\n\n### Firewall Patterns\n\n**Organization-Scoped:**\n```sql\nCREATE POLICY \"projects_select\" ON projects FOR SELECT\nUSING (organization_id = public.get_active_org_id());\n```\n\n**User-Scoped:**\n```sql\nCREATE POLICY \"preferences_select\" ON preferences FOR SELECT\nUSING (user_id = auth.user_id());\n```\n\n**Public Tables:**\n```sql\nCREATE POLICY \"categories_deny_anon\" ON categories FOR ALL TO anon USING (false);\nCREATE POLICY \"categories_all\" ON categories FOR ALL TO authenticated USING (true) WITH CHECK (true);\n```\n\n## Generated Helper Functions\n\n| Function | Purpose |\n|----------|---------|\n| `get_active_org_id()` | Returns user's active organization from `user_sessions` |\n| `has_any_role(roles[])` | Checks if user has any specified role |\n| `has_org_role(role)` | Checks for a specific role |\n| `is_org_member()` | Checks org membership |\n| `is_owner(owner_id)` | Checks record ownership |\n\n## Generated Files\n\n```\
|
|
238
|
+
"content": "Quickback supports Neon as a PostgreSQL database provider, generating schemas with Row Level Security (RLS) policies that enforce your firewall configuration at the database level.\n\n## Why Neon?\n\n- **Full PostgreSQL** — Advanced queries, joins, PostGIS, full-text search\n- **Database-Level Security** — RLS policies enforce access even if API is bypassed\n- **Serverless Architecture** — HTTP connection mode for Cloudflare Workers\n- **Neon Authorize** — JWT-based RLS with `auth.user_id()` function\n- **Better Auth Integration** — Works seamlessly with Better Auth\n\n## Configuration\n\n```typescript\n\nexport default defineConfig({\n name: \"my-app\",\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"neon\", {\n connectionMode: 'auto', // auto-detects based on runtime\n pooled: true,\n }),\n auth: defineAuth(\"better-auth\"),\n },\n});\n```\n\n### Environment Variables\n\n```bash\nDATABASE_URL=postgresql://user:pwd@ep-xxx.neon.tech/db?sslmode=require\n```\n\n## Row Level Security\n\nQuickback generates RLS policies from your firewall config:\n\n```typescript\nfirewall: {\n organization: {},\n owner: { mode: 'optional' }\n}\n```\n\n```sql\n-- Generated policy (uses auth.user_id() for Neon)\nCREATE POLICY \"documents_select\" ON documents FOR SELECT\nUSING (\n organization_id = get_active_org_id()\n AND (has_any_role(ARRAY['admin']) OR user_id = auth.user_id())\n);\n```\n\n### Firewall Patterns\n\n**Organization-Scoped:**\n```sql\nCREATE POLICY \"projects_select\" ON projects FOR SELECT\nUSING (organization_id = public.get_active_org_id());\n```\n\n**User-Scoped:**\n```sql\nCREATE POLICY \"preferences_select\" ON preferences FOR SELECT\nUSING (user_id = auth.user_id());\n```\n\n**Public Tables:**\n```sql\nCREATE POLICY \"categories_deny_anon\" ON categories FOR ALL TO anon USING (false);\nCREATE POLICY \"categories_all\" ON categories FOR ALL TO authenticated USING (true) WITH CHECK (true);\n```\n\n## Generated Helper Functions\n\n| Function | Purpose |\n|----------|---------|\n| `get_active_org_id()` | Returns user's active organization from `user_sessions` |\n| `has_any_role(roles[])` | Checks if user has any specified role |\n| `has_org_role(role)` | Checks for a specific role |\n| `is_org_member()` | Checks org membership |\n| `is_owner(owner_id)` | Checks record ownership |\n\n## Generated Files\n\n```\nquickback/drizzle/\n├── migrations/\n│ ├── 0100_create_rls_helpers.sql\n│ ├── 0101_create_rls_policies.sql\n│ └── 0102_create_indexes.sql\nsrc/\n├── db/\n│ └── schema.ts\n├── auth/\n│ └── schema.ts\n└── lib/\n └── neon.ts\n```\n\n## Setting Up Neon Authorize\n\n1. Go to **Project Settings > Authorize** in Neon console\n2. Add your JWKS URL: `https://your-api.com/auth/v1/.well-known/jwks.json`\n3. The `auth.user_id()` function will return the authenticated user's ID from JWT\n\n## Getting Started\n\n1. Create a Neon project at [neon.tech](https://neon.tech)\n2. Configure your Quickback project for Neon\n3. Run `quickback compile`\n4. Apply migrations with `drizzle-kit migrate`"
|
|
239
239
|
},
|
|
240
240
|
"compiler/integrations/supabase": {
|
|
241
241
|
"title": "Supabase",
|
|
@@ -279,7 +279,7 @@ export const DOCS = {
|
|
|
279
279
|
},
|
|
280
280
|
"index": {
|
|
281
281
|
"title": "Quickback Documentation",
|
|
282
|
-
"content": "# Quickback Documentation\n\nQuickback is a **backend compiler**. Define your database schema and security rules in TypeScript — Quickback compiles them into a complete, production-ready API.\n\n## Quick Start\n\n```bash\nnpm install -g @kardoe/quickback\nquickback create cloudflare my-app\ncd my-app\nquickback compile\n```\n\n## How It Works\n\nWrite ~50 lines of configuration. Get 500+ lines of production code.\n\n```\ndefineTable() → Compiler → Production API\n │ │\n ├── Schema ├── CRUD routes + batch operations\n ├── Firewall ├── Auth middleware\n ├── Access ├── Data isolation queries\n ├── Guards ├── Field validation\n └── Masking ├── PII redaction\n ├── TypeScript types\n └── Database migrations\n```\n\nEvery API request passes through four security layers:\n\n```\nRequest → Firewall → Access → Guards → Masking → Response\n │ │ │ │\n │ │ │ └── Hide sensitive fields\n │ │ └── Block field modifications\n │ └── Check roles & conditions\n └── Isolate data by owner/org\n```\n\n**Secure by default.** Nothing is accessible until you explicitly open it up. See [Definitions](/compiler/definitions) for the full security model.\n\n---\n\n## Products\n\n### [Quickback
|
|
282
|
+
"content": "# Quickback Documentation\n\nQuickback is a **backend compiler**. Define your database schema and security rules in TypeScript — Quickback compiles them into a complete, production-ready API or Supabase RLS policies.\n\n## Quick Start\n\n```bash\nnpm install -g @kardoe/quickback\nquickback create cloudflare my-app\ncd my-app\nquickback compile\n```\n\n## How It Works\n\nWrite ~50 lines of configuration. Get 500+ lines of production code.\n\n```\ndefineTable() → Compiler → Production API\n │ │\n ├── Schema ├── CRUD routes + batch operations\n ├── Firewall ├── Auth middleware\n ├── Access ├── Data isolation queries\n ├── Guards ├── Field validation\n └── Masking ├── PII redaction\n ├── TypeScript types\n └── Database migrations\n```\n\nEvery API request passes through four security layers:\n\n```\nRequest → Firewall → Access → Guards → Masking → Response\n │ │ │ │\n │ │ │ └── Hide sensitive fields\n │ │ └── Block field modifications\n │ └── Check roles & conditions\n └── Isolate data by owner/org\n```\n\n**Secure by default.** Nothing is accessible until you explicitly open it up. See [Definitions](/compiler/definitions) for the full security model.\n\n---\n\n## Products\n\n### [Quickback API](/compiler)\n\nThe multi-tenant-first API. Define your schema and security rules in TypeScript — the compiler generates a full Hono API with four built-in security layers.\n\n- **[Getting Started](/compiler/getting-started)** — Create your first project\n- **[Definitions](/compiler/definitions)** — Schema, Firewall, Access, Guards, Masking, Views, Actions\n- **[Using the API](/compiler/using-the-api)** — CRUD endpoints and filtering\n- **[Cloud Compiler](/compiler/cloud-compiler)** — CLI, MCP Server, authentication, endpoints\n\n**Compile targets:** Cloudflare D1 (recommended) · Neon\n\n---\n\n### Quickback for Supabase RLS\n\nAlready on Supabase? Quickback compiles your TypeScript definitions into Row Level Security policies. Keep Supabase Auth, Storage, and Realtime — Quickback just adds the security layer.\n\n- **[Getting Started](/compiler/getting-started)** — Same definitions, target `supabase`\n- **[Definitions](/compiler/definitions)** — Firewall and Access compile to `FOR ALL USING` and `FOR SELECT/INSERT/UPDATE/DELETE USING` policies\n\n**Compile target:** Supabase (RLS policies only)\n\n---\n\n### [Quickback Stack](/stack)\n\nThe complete Supabase alternative on Cloudflare. Includes Quickback API plus realtime, storage, vector search, queues, and email — all running on your own Cloudflare account.\n\n- **[Auth](/stack/auth)** — Better Auth plugins, security, device auth, API keys\n- **[Database](/stack/database)** — D1, Neon\n- **[Storage](/stack/storage)** — KV, R2\n- **[Realtime](/stack/realtime)** — Durable Objects + WebSocket\n- **[Queues](/stack/queues)** — Background jobs and webhooks\n- **[Vector & AI](/stack/vector)** — Embeddings, semantic search\n\n---\n\n### [Account UI](/account-ui)\n\nPre-built authentication and account management UI. Deploy to Cloudflare Workers.\n\n- **[Features](/account-ui)** — Sessions, organizations, passkeys, admin panel\n- **[Customization](/account-ui/customization)** — Branding, theming, feature flags\n\n---\n\n### [Plugins & Tools](/plugins-tools)\n\nOpen-source Better Auth plugins and developer tools.\n\n- **[Better Auth Plugins](/plugins-tools)** — AWS SES, combo auth, upgrade anonymous\n- **[Claude Code Skill](/plugins-tools/claude-code-skill)** — AI-powered Quickback assistance\n- **MCP Server** — Connect any MCP-compatible AI tool to Quickback\n\n### [Changelog](/changelog)\n\nWhat's new in Quickback — release notes, new features, and improvements."
|
|
283
283
|
},
|
|
284
284
|
"plugins-tools/better-auth-plugins/aws-ses": {
|
|
285
285
|
"title": "AWS SES Plugin",
|
|
@@ -331,7 +331,7 @@ export const DOCS = {
|
|
|
331
331
|
},
|
|
332
332
|
"stack/database/d1": {
|
|
333
333
|
"title": "D1 Database",
|
|
334
|
-
"content": "Cloudflare D1 is SQLite at the edge. Quickback uses D1 as the primary database for Cloudflare deployments, with a multi-database pattern for separation of concerns.\n\n## What is D1?\n\nD1 is Cloudflare's serverless SQL database built on SQLite:\n\n- **Edge-native** - Runs in Cloudflare's global network\n- **SQLite compatible** - Use familiar SQL syntax\n- **Zero configuration** - No connection strings or pooling\n- **Automatic replication** - Read replicas at every edge location\n\n## Multi-Database Pattern\n\nQuickback generates separate D1 databases for different concerns:\n\n| Database | Binding | Purpose |\n|----------|---------|---------|\n| `AUTH_DB` | `AUTH_DB` | Better Auth tables (user, session, account) |\n| `DB` | `DB` | Your application data |\n| `FILES_DB` | `FILES_DB` | File metadata for R2 uploads |\n| `WEBHOOKS_DB` | `WEBHOOKS_DB` | Webhook delivery tracking |\n\nThis separation provides:\n- **Independent scaling** - Auth traffic doesn't affect app queries\n- **Isolation** - Auth schema changes don't touch your data\n- **Clarity** - Clear ownership of each database\n\n## Drizzle ORM Integration\n\nQuickback uses [Drizzle ORM](https://orm.drizzle.team/) for type-safe database access:\n\n```ts\n// schema/tables.ts - Your schema definition\n\nexport const posts = sqliteTable(\"posts\", {\n id: text(\"id\").primaryKey(),\n title: text(\"title\").notNull(),\n content: text(\"content\"),\n authorId: text(\"author_id\").notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp\" }),\n});\n```\n\nThe compiler generates Drizzle queries based on your security rules:\n\n```ts\n// Generated query with firewall applied\nconst result = await db\n .select()\n .from(posts)\n .where(eq(posts.authorId, userId)); // Firewall injects ownership\n```\n\n## Migrations\n\nQuickback generates migrations automatically at compile time based on your schema changes:\n\n```bash\n# Compile your project (generates migrations)\nquickback compile\n\n# Apply migrations locally\nwrangler d1 migrations apply DB --local\n\n# Apply to production D1\nwrangler d1 migrations apply DB --remote\n```\n\nMigration files are generated in `drizzle
|
|
334
|
+
"content": "Cloudflare D1 is SQLite at the edge. Quickback uses D1 as the primary database for Cloudflare deployments, with a multi-database pattern for separation of concerns.\n\n## What is D1?\n\nD1 is Cloudflare's serverless SQL database built on SQLite:\n\n- **Edge-native** - Runs in Cloudflare's global network\n- **SQLite compatible** - Use familiar SQL syntax\n- **Zero configuration** - No connection strings or pooling\n- **Automatic replication** - Read replicas at every edge location\n\n## Multi-Database Pattern\n\nQuickback generates separate D1 databases for different concerns:\n\n| Database | Binding | Purpose |\n|----------|---------|---------|\n| `AUTH_DB` | `AUTH_DB` | Better Auth tables (user, session, account) |\n| `DB` | `DB` | Your application data |\n| `FILES_DB` | `FILES_DB` | File metadata for R2 uploads |\n| `WEBHOOKS_DB` | `WEBHOOKS_DB` | Webhook delivery tracking |\n\nThis separation provides:\n- **Independent scaling** - Auth traffic doesn't affect app queries\n- **Isolation** - Auth schema changes don't touch your data\n- **Clarity** - Clear ownership of each database\n\n## Drizzle ORM Integration\n\nQuickback uses [Drizzle ORM](https://orm.drizzle.team/) for type-safe database access:\n\n```ts\n// schema/tables.ts - Your schema definition\n\nexport const posts = sqliteTable(\"posts\", {\n id: text(\"id\").primaryKey(),\n title: text(\"title\").notNull(),\n content: text(\"content\"),\n authorId: text(\"author_id\").notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp\" }),\n});\n```\n\nThe compiler generates Drizzle queries based on your security rules:\n\n```ts\n// Generated query with firewall applied\nconst result = await db\n .select()\n .from(posts)\n .where(eq(posts.authorId, userId)); // Firewall injects ownership\n```\n\n## Migrations\n\nQuickback generates migrations automatically at compile time based on your schema changes:\n\n```bash\n# Compile your project (generates migrations)\nquickback compile\n\n# Apply migrations locally\nwrangler d1 migrations apply DB --local\n\n# Apply to production D1\nwrangler d1 migrations apply DB --remote\n```\n\nMigration files are generated in `quickback/drizzle/` and version-controlled with your code. You never need to manually generate migrations—just define your schema and compile.\n\n## wrangler.toml Bindings\n\nConfigure D1 bindings in `wrangler.toml`:\n\n```toml\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"my-app-db\"\ndatabase_id = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n\n[[d1_databases]]\nbinding = \"AUTH_DB\"\ndatabase_name = \"my-app-auth\"\ndatabase_id = \"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\"\n\n[[d1_databases]]\nbinding = \"FILES_DB\"\ndatabase_name = \"my-app-files\"\ndatabase_id = \"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz\"\n```\n\nCreate databases via Wrangler:\n\n```bash\nwrangler d1 create my-app-db\nwrangler d1 create my-app-auth\nwrangler d1 create my-app-files\n```\n\n## Accessing Data via API\n\nAll data access in Quickback goes through the generated API endpoints—never direct database queries. This ensures security rules (firewall, access, guards, masking) are always enforced.\n\n### CRUD Operations\n\n```bash\n# List posts (firewall automatically filters by ownership)\nGET /api/v1/posts\n\n# Get a single post\nGET /api/v1/posts/:id\n\n# Create a post (guards validate allowed fields)\nPOST /api/v1/posts\n{ \"title\": \"Hello World\", \"content\": \"...\" }\n\n# Update a post (guards validate updatable fields)\nPATCH /api/v1/posts/:id\n{ \"title\": \"Updated Title\" }\n\n# Delete a post\nDELETE /api/v1/posts/:id\n```\n\n### Filtering and Pagination\n\n```bash\n# Filter by field\nGET /api/v1/posts?status=published\n\n# Pagination\nGET /api/v1/posts?limit=10&offset=20\n\n# Sort\nGET /api/v1/posts?sort=createdAt&order=desc\n```\n\n### Why No Direct Database Access?\n\nDirect database queries bypass Quickback's security layers:\n- **Firewall** - Data isolation by user/org/team\n- **Access** - Role-based permissions\n- **Guards** - Field-level create/update restrictions\n- **Masking** - Sensitive data redaction\n\nAlways use the API endpoints. For custom business logic, use [Actions](/compiler/definitions/actions).\n\n## Local Development\n\nD1 works locally with Wrangler:\n\n```bash\n# Start local dev server with D1\nwrangler dev\n\n# D1 data persists in .wrangler/state/\n```\n\nLocal D1 uses SQLite files in `.wrangler/state/v3/d1/`, which you can inspect with any SQLite client.\n\n## Security Architecture\n\nFor all Quickback-generated code, D1's application-layer security provides equivalent protection to Supabase RLS. The key difference is where enforcement happens — and this matters if you write custom routes.\n\n### How Security Works\n\n| Component | Enforcement | Notes |\n|-----------|-------------|-------|\n| CRUD endpoints | ✅ Firewall auto-applied | All generated routes enforce security |\n| Actions | ✅ Firewall auto-applied | Both standalone and record-based |\n| Manual routes | ⚠️ Must apply firewall | Use `withFirewall` helper |\n\n### Why D1 is Secure\n\n1. **No external database access** - D1 can only be queried through your Worker. There's no connection string or external endpoint.\n2. **Generated code enforces rules** - All CRUD and Action endpoints automatically apply firewall, access, guards, and masking.\n3. **Single entry point** - Every request flows through your API where security is enforced.\n\nUnlike Supabase where PostgreSQL RLS provides database-level enforcement (protecting data even if application code has bugs), D1's security comes from architecture: the database is inaccessible except through your Worker, and all generated routes apply the four security pillars. The trade-off is that **custom routes you write outside Quickback must manually apply security** — there is no database-level safety net.\n\n### Comparison with Supabase RLS\n\n| Scenario | Supabase | D1 |\n|----------|----------|-----|\n| CRUD endpoints | ✅ Secure (RLS + App) | ✅ Secure (App) |\n| Actions | ✅ Secure (RLS + App) | ✅ Secure (App) |\n| Manual routes | ✅ RLS still protects | ⚠️ Must apply firewall |\n| External DB access | ⚠️ Possible with credentials | ✅ Not possible |\n| Dashboard queries | Via Supabase Studio | ⚠️ Admin only (audit logged) |\n\n### Writing Manual Routes\n\nIf you write custom routes outside of Quickback compilation (e.g., custom reports, integrations), use the generated `withFirewall` helper to ensure security:\n\n```ts\n\napp.get('/reports/monthly', async (c) => {\n return withFirewall(c, async (ctx, firewall) => {\n const results = await db.select()\n .from(invoices)\n .where(firewall);\n return c.json(results);\n });\n});\n```\n\nThe `withFirewall` helper:\n- Validates authentication\n- Builds the correct WHERE conditions for the current user/org\n- Returns 401 if not authenticated\n\n### Best Practices\n\n1. **Use generated endpoints** - Prefer CRUD and Actions over manual routes\n2. **Always apply firewall** - When writing manual routes, always use `withFirewall`\n3. **Avoid raw SQL** - Raw SQL bypasses application security; use Drizzle ORM\n4. **Review custom code** - Manual routes should be code-reviewed for security\n\n## Limitations\n\nD1 is built on SQLite, which means:\n\n- **No stored procedures** - Business logic lives in your Worker\n- **Single-writer** - One write connection at a time (reads scale horizontally)\n- **Size limits** - 10GB per database (free tier: 500MB)\n- **No PostgreSQL extensions** - Use Supabase if you need PostGIS, etc.\n\nFor most applications, these limits are non-issues. D1's edge distribution and zero-config setup outweigh the constraints."
|
|
335
335
|
},
|
|
336
336
|
"stack/database": {
|
|
337
337
|
"title": "Database",
|