Connecting
Run the CMS in demo mode with mock data or connect it to a live Quickback API.
Connecting
The CMS supports two modes: demo mode for development and testing, and live mode for connecting to a real Quickback API.
Demo Mode
When no VITE_API_URL is set, the CMS runs in demo mode:
- Uses a mock API client backed by
localStorage - Loads seed data from JSON files in
data/mock/ - Provides a role switcher in the header to test owner/admin/member access
- Simulates authentication sessions without a real backend
Demo mode is useful for prototyping your schema, testing guard behavior across roles, and verifying masking rules before deploying.
# No VITE_API_URL — CMS runs in demo modeMock Data
Place JSON files in data/mock/ matching your table names:
data/mock/
contact.json # Array of contact records
project.json # Array of project records
invoice.json # Array of invoice records
_meta.json # Mock session and org metadataEach file contains an array of records. The mock client loads them on startup and persists changes to localStorage.
Role Switcher
In demo mode, the header displays a role switcher dropdown allowing you to switch between owner, admin, and member roles in real time. This lets you verify:
- Which CRUD buttons appear per role
- Which form fields are editable
- Which masking rules apply
- Which actions are available
- Which views are accessible
Live Mode
Set VITE_API_URL to connect to a real Quickback API:
VITE_API_URL=https://api.example.comIn live mode, the CMS:
- Reads the Better Auth session from cookies
- Fetches the user's organization membership and role
- Makes real API calls to the Quickback backend for all CRUD operations
- Applies server-side security (firewall, guards, masking) in addition to client-side UI filtering
API Client Interface
The CMS communicates with the backend through a standard interface:
interface IApiClient {
list(table: string, params?: ListParams): Promise<ListResult>;
get(table: string, id: string): Promise<Record | null>;
create(table: string, data: Record): Promise<Record>;
update(table: string, id: string, data: Partial<Record>): Promise<Record>;
delete(table: string, id: string): Promise<void>;
executeAction(
table: string,
actionName: string,
recordId: string | null,
input: Record
): Promise<Record>;
listView(
table: string,
viewName: string,
params?: ListParams
): Promise<ListResult>;
}List Parameters
interface ListParams {
page?: number;
pageSize?: number;
sort?: string;
order?: "asc" | "desc";
search?: string;
filters?: Record<string, unknown>;
}List Result
interface ListResult {
data: Record[];
total: number;
page: number;
pageSize: number;
}Both the mock client and the live client implement this same interface. The CMS code is identical in both modes — only the underlying transport changes.
Server-Side Security
In live mode, all four Quickback security layers (firewall, CRUD access, guards, masking) are enforced server-side. The CMS hides unauthorized UI elements as a convenience, but the API rejects unauthorized requests regardless.
How the Client is Created
The CMS auto-creates the API client based on configuration:
- No
VITE_API_URL— Instantiates the mock client, loads seed data fromdata/mock/, and useslocalStoragefor persistence. VITE_API_URLis set — Instantiates the live client, reads the auth session cookie, and makes fetch requests to the Quickback API using the standard REST endpoints (/api/v1/{table}).
The client is provided via React context (ApiClientContext) and available throughout the component tree.
Next Steps
- Table Views — Browse and Data Table view modes
- Security — How roles, guards, and masking work in the CMS