Quickback Docs

Single-Tenant Mode

Build admin-only or public-facing apps without multi-tenant organization overhead.

Single-tenant mode disables the Better Auth organization plugin, removing the organizationId requirement from your API. This is ideal for personal sites, blogs, admin panels, or any app with a single tenant.

Enabling Single-Tenant Mode

Set features.organizations to false in your config:

import { defineConfig, defineRuntime, defineDatabase, defineAuth } from "@quickback/compiler";

export default defineConfig({
  name: "my-site",
  template: "hono",
  features: {
    organizations: false,
  },
  providers: {
    runtime: defineRuntime("cloudflare"),
    database: defineDatabase("cloudflare-d1"),
    auth: defineAuth("better-auth"),
  },
});

What Changes

AspectMulti-Tenant (default)Single-Tenant
Organization pluginEnabledDisabled
organizationId columnsRequired on resourcesNot used
Role sourceOrg membership tableuser.role field
Firewall scopesowner, organization, team, exceptionowner, exception only
Admin org endpoints/admin/v1/organizations generatedSkipped
ctx.activeOrgIdFrom session/JWTAlways undefined
ctx.rolesFrom org member roleFrom user.role

Firewall Configuration

In single-tenant mode, resources use either owner scope (user-scoped data) or exception (public/system data):

// User-scoped data (requires authentication)
export default defineTable(posts, {
  firewall: { owner: {} },
  crud: {
    list: { access: { roles: ['user', 'admin'] } },
    create: { access: { roles: ['admin'] } },
  },
});

// Public data (no ownership filtering)
export default defineTable(pages, {
  firewall: { exception: true },
  crud: {
    list: { access: { roles: ['PUBLIC'] } },
    get: { access: { roles: ['PUBLIC'] } },
    create: { access: { roles: ['admin'] } },
    update: { access: { roles: ['admin'] } },
    delete: { access: { roles: ['admin'] } },
  },
});

Using firewall: { organization: {} } or firewall: { team: {} } with organizations: false will cause a compile error. These scopes require the organization plugin.

Public Routes

Use the PUBLIC role to expose routes without authentication. This works the same as in multi-tenant mode:

crud: {
  list: { access: { roles: ['PUBLIC'] } },  // No auth required
  get: { access: { roles: ['PUBLIC'] } },   // No auth required
  create: { access: { roles: ['admin'] } }, // Admin only
}

Role-Based Access

Roles come from the user.role field (managed by Better Auth's admin plugin):

RoleDescription
adminFull access, set via Better Auth admin plugin
userDefault role for authenticated users
PUBLICUnauthenticated access (special keyword)
// Admin-only action
export default defineActions({
  publish: {
    access: { roles: ['admin'] },
    execute: async ({ ctx, db }) => {
      // ctx.userRole === 'admin'
      // ctx.roles === ['admin']
    },
  },
});

Consuming the API

Since single-tenant mode produces a JSON API, pair it with any frontend framework:

// Astro, Next.js, SvelteKit, etc.
const posts = await fetch('https://api.mysite.com/api/v1/posts').then(r => r.json());

// Admin operations require authentication
const res = await fetch('https://api.mysite.com/api/v1/posts', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ title: 'New Post', content: '...' }),
});

On this page