Quickback Docs

Schema Registry

The JSON metadata file generated by the Quickback compiler that powers the CMS.

Schema Registry

The schema registry is a static JSON file generated by the Quickback compiler. It contains full metadata about every table in your project — columns, types, guards, masking rules, views, actions, validation, and firewall config. The CMS reads this file to render its entire UI.

Generation

Schema registry generation is enabled by default. Run quickback compile and the compiler outputs schema-registry.json alongside your compiled API files.

To disable generation:

quickback/quickback.config.ts
export default defineConfig({
  schemaRegistry: { generate: false },
  // ... rest of your config
});

Output Shape

The top-level structure of schema-registry.json:

{
  "generatedAt": "2026-02-14T06:26:53.554Z",
  "generatedBy": "quickback-compiler",
  "version": "1.0.0",
  "features": { ... },
  "tables": { ... },
  "tablesByFeature": { ... }
}
FieldTypeDescription
generatedAtstringISO timestamp of generation
generatedBystringAlways "quickback-compiler"
versionstringCompiler version used
featuresRecord<string, string[]>Feature name to file list mapping
tablesRecord<string, TableMeta>Table name to full metadata
tablesByFeatureRecord<string, string[]>Feature name to table name list

TableMeta

Each table entry contains everything the CMS needs to render its UI:

interface TableMeta {
  name: string;              // camelCase table name (e.g., "accountCode")
  dbName: string;            // SQL table name (e.g., "account_code")
  feature: string;           // Parent feature name
  columns: ColumnMeta[];     // All columns including audit fields
  firewall: Record<string, unknown>;  // Tenant isolation config
  crud: Record<string, CrudConfig>;   // Per-operation access rules
  guards: {
    createable: string[];    // Fields allowed on create
    updatable: string[];     // Fields allowed on update
    immutable: string[];     // Fields locked after creation
    protected: Record<string, string[]>;  // Fields only updatable via actions
  };
  masking: Record<string, MaskingRule>;  // Per-field masking rules
  views: Record<string, ViewConfig>;     // Named column projections
  validation: Record<string, ValidationRule>;  // Per-field validation
  actions: ActionMeta[];     // Available actions for this table
  displayColumn?: string;    // Human-readable label column
  internal?: boolean;        // Hidden from CMS sidebar when true
}

Internal Tables

Tables without a defineTable() resource config are marked internal: true and hidden from the CMS sidebar. These are typically join tables or system tables.

ColumnMeta

Each column in the columns array:

interface ColumnMeta {
  name: string;        // Property name (camelCase)
  dbName: string;      // SQL column name (snake_case)
  type: "text" | "integer" | "real" | "blob";  // SQLite type
  mode?: "boolean";    // When an integer represents a boolean
  primaryKey: boolean;
  notNull: boolean;
  defaultValue?: string | number | boolean;
  fkTarget?: string;   // Target table name for FK columns
}

The compiler automatically includes audit fields (id, organizationId, createdAt, createdBy, modifiedAt, modifiedBy, deletedAt) at the beginning of every table's column list.

fkTarget — FK Resolution

Columns ending in Id may have a fkTarget property indicating which table they reference. This is resolved in priority order:

  1. Explicit references — from your defineTable() config (highest priority)
  2. Drizzle .references() — parsed from schema source code
  3. Convention — strip Id suffix, match table name directly
{
  "name": "vendorId",
  "type": "text",
  "fkTarget": "contact"
}

The CMS uses fkTarget to render typeahead/lookup inputs that search the correct table instead of showing raw IDs.

Input Hints

Tables with inputHints configured in defineTable() include an inputHints map in their metadata:

{
  "name": "invoice",
  "inputHints": {
    "status": "select",
    "sortOrder": "radio",
    "isPartialPaymentDisabled": "checkbox",
    "headerMessage": "textarea"
  }
}

The CMS reads these hints to render the appropriate form control for each field. See Input Hints for the full list of supported values.

Display Column

The displayColumn field tells the CMS which column to use as a human-readable label for a record. This is used in:

  • FK typeahead dropdowns (showing names instead of IDs)
  • Record titles in detail views
  • Breadcrumb labels

Auto-Detection

If you don't explicitly set displayColumn in your resource config, the compiler auto-detects it by scanning column names in priority order:

  1. name
  2. title
  3. label
  4. headline
  5. subject
  6. code
  7. displayName
  8. fullName
  9. description

The first match wins. If no candidate matches, the table has no display column and the CMS falls back to showing IDs.

Explicit Config

Set it explicitly in your table definition:

export default defineTable(contacts, {
  displayColumn: "companyName",
  // ...
});

FK Label Resolution

When a table has foreign key columns (ending in Id), the API enriches list responses with _label fields. For example, a roomTypeId column gets a corresponding roomType_label field containing the display column value from the referenced table.

The CMS uses these _label fields to show human-readable names in table cells and FK typeahead dropdowns instead of raw UUIDs.

roomTypeId: "rt_abc123"        → displayed as "Master Bedroom"
accountCodeId: "ac_xyz789"     → displayed as "4100 - Revenue"

The FK target table is resolved from the fkTarget property on each column. For simple cases like roomTypeIdroomType, the compiler auto-detects it. For non-obvious mappings (e.g., vendorIdcontact), use explicit references in your defineTable() config.

Next Steps

On this page