Skip to content

Schema Protocol

The admin app is entirely driven by a JSON schema served from GET /admin/schema. This endpoint is auto-generated by Craft Easy API based on your registered resources, field definitions, and access rights.

AdminSchema

The root schema object describes the entire admin interface:

interface AdminSchema {
  version: string;          // Schema version, e.g. "1.0"
  title: string;            // App title, e.g. "My Admin"
  auth: AuthConfig;         // Authentication endpoints
  modules: Record<string, boolean>; // Enabled modules
  theme?: ThemeConfig;      // Optional theme overrides
  navigation?: NavigationGroup[];   // Sidebar navigation groups
  resources: ResourceSchema[];      // All available resources
}

AuthConfig

interface AuthConfig {
  login_endpoint: string;     // e.g. "/auth/login/email"
  refresh_endpoint: string;   // e.g. "/auth/refresh"
  token_header: string;       // e.g. "X-API-Key"
}

ThemeConfig

interface ThemeConfig {
  primary_color: string;   // CSS color, e.g. "#2563eb"
  logo_url?: string;       // URL to logo image
  favicon_url?: string;    // URL to favicon
}

Groups resources in the sidebar:

interface NavigationGroup {
  label: string;        // Group heading, e.g. "Users & Access"
  icon?: string;        // Optional icon identifier
  resources: string[];  // Resource names in this group
}

ResourceSchema

Each resource describes one API collection and how the admin should display it:

interface ResourceSchema {
  endpoint: string;       // API path, e.g. "/users"
  name: string;           // Unique identifier, e.g. "users"
  label: string;          // Singular display name, e.g. "User"
  label_plural: string;   // Plural display name, e.g. "Users"
  icon?: string;          // Optional icon
  description?: string;   // Tooltip/help text
  methods: string[];      // Allowed HTTP methods: ["GET","POST","PATCH","DELETE"]
  list: ListConfig;       // List view configuration
  form: FormConfig;       // Create/edit form configuration
  fields: Record<string, FieldSchema>;  // All field definitions
  features: ResourceFeatures;           // Enabled UI features
}

ListConfig

Controls how the list view displays items:

interface ListConfig {
  fields: string[];          // Columns to show
  search_fields: string[];   // Fields included in search
  filter_fields: string[];   // Fields available as filters
  sort_default: string;      // Default sort field (prefix "-" for descending)
  sort_fields?: string[];    // Sortable fields
  page_size: number;         // Items per page
}

FormConfig

Organizes form fields into collapsible groups:

interface FormConfig {
  groups: FormGroup[];
}

interface FormGroup {
  label: string;          // Group heading
  fields: string[];       // Fields in this group
  collapsible?: boolean;  // Can be collapsed
  collapsed?: boolean;    // Initially collapsed
}

ResourceFeatures

Toggles for UI capabilities:

interface ResourceFeatures {
  create: boolean;       // Show "Create" button
  edit: boolean;         // Show "Edit" button
  delete: boolean;       // Show "Delete" button
  export: boolean;       // Show "Export" button
  bulk_delete: boolean;  // Allow bulk selection + delete
  duplicate: boolean;    // Show "Duplicate" action
}

FieldSchema

Each field definition describes the data type, widget, validation, and display:

interface FieldSchema {
  // Type & display
  type: string;              // "string" | "number" | "boolean" | "date" | "json"
  widget: string;            // Widget identifier (see Field Widgets)
  required: boolean;
  readonly: boolean;
  label?: string;            // Display label (auto-generated from field name if omitted)
  placeholder?: string;
  help_text?: string;

  // Validation
  min?: number;              // Minimum value (numbers) or min date
  max?: number;              // Maximum value (numbers) or max date
  step?: number;             // Increment step for number inputs
  min_length?: number;       // Minimum string length
  max_length?: number;       // Maximum string length
  pattern?: string;          // Regex validation pattern

  // Select options
  options?: SelectOption[];  // For select/multiselect widgets

  // Relations
  relation?: RelationConfig; // For relation picker widget

  // File upload
  accept?: string;           // MIME types, e.g. "image/*,.pdf"
  max_size_mb?: number;      // Max file size in MB

  // GDPR
  gdpr?: boolean;            // Field contains personal data
  gdpr_category?: string;    // PII category
}

SelectOption

interface SelectOption {
  value: string;
  label: string;
  color?: string;   // Optional badge color
  icon?: string;    // Optional icon
}

RelationConfig

Configures the relation picker for foreign key fields:

interface RelationConfig {
  endpoint: string;       // Related resource endpoint, e.g. "/tenants"
  label_field: string;    // Field to display, e.g. "name"
  search_field: string;   // Field to search on, e.g. "name"
  value_field: string;    // Field to store, e.g. "id"
  filters?: Record<string, string>;  // Static filters applied to queries
}

How the Schema Is Generated

The /admin/schema endpoint is automatically built from your registered resources:

  1. Resource registry — every resource registered via app.register_resource() is included
  2. Field introspection — Pydantic model fields are converted to FieldSchema with type, widget, and validation rules inferred from type annotations and Field() metadata
  3. Access filtering — the schema is filtered per-user based on access rights:
    • System users see all resources
    • Other users only see resources they have GET access to
    • Fields with attribute_access level "none" are removed
    • Fields with "read" level become readonly: true
    • features.create, features.edit, features.delete are adjusted based on allowed methods
  4. ETag caching — an MD5 hash of the schema JSON is returned as an ETag header; clients can send If-None-Match to get a 304 Not Modified response
# Example: the schema endpoint automatically reflects your models
@app.get("/admin/schema")
async def get_admin_schema(request: Request):
    schema = resource_registry.build_admin_schema()
    # Filter by user's access rights...
    return schema