RaisFastRaisFast
Content Types

Schema Management

CLI commands, Admin API, hot-reload, and database migration strategy.

Manage content type schemas via the CLI, Admin API, or by editing TOML files directly. Changes take effect without restarting the server.

CLI Commands

Create a Content Type

raisfast ct new course

Generates a scaffold TOML file at extensions/content_types/course.toml with the basic structure.

Validate Schemas

raisfast ct check

Validates all TOML files in the content types directory. Reports errors for:

  • Missing required fields (name, singular, plural, table)
  • Invalid field types
  • Duplicate table names or reserved names
  • Circular relation references

Generate TypeScript Types

raisfast ct types course -o types/course.ts

Generates TypeScript interfaces from a content type definition, including field types, optional markers, and relation types.

Admin API

Manage schemas at runtime via the Admin API:

GET    /api/v1/admin/content-types              List all schemas
GET    /api/v1/admin/content-types/{singular}   Get a single schema
POST   /api/v1/admin/content-types              Create schema
PUT    /api/v1/admin/content-types/{singular}   Update schema
DELETE /api/v1/admin/content-types/{singular}   Delete schema

Create a Schema

curl -X POST http://localhost:9898/api/v1/admin/content-types \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Project",
    "singular": "project",
    "plural": "projects",
    "table": "projects",
    "fields": [
      {"name": "title", "field_type": "text", "required": true},
      {"name": "status", "field_type": "enum", "enum_values": ["active", "completed"], "default": "active"}
    ],
    "protocols": ["timestampable", "ownable"]
  }'

This single request:

  1. Validates the schema
  2. Writes the TOML file to extensions/content_types/
  3. Migrates the database (creates the projects table)
  4. Registers the routes in memory

Update a Schema

curl -X PUT http://localhost:9898/api/v1/admin/content-types/project \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": [
      {"name": "title", "field_type": "text", "required": true},
      {"name": "status", "field_type": "enum", "enum_values": ["active", "completed", "archived"], "default": "active"},
      {"name": "priority", "field_type": "integer", "min": 1, "max": 5}
    ]
  }'

This:

  1. Updates the TOML file
  2. Migrates the database (adds the new priority column via ALTER TABLE)
  3. Re-registers the schema in memory

Delete a Schema

curl -X DELETE http://localhost:9898/api/v1/admin/content-types/project \
  -H "Authorization: Bearer TOKEN"

This unregisters the routes and removes the TOML file. The database table is preserved — no data loss.

Hot-Reload

Content type schemas are loaded into memory using lock-free ArcSwap, enabling hot-reload without downtime:

  • TOML file changes are picked up on the next request (when using the file watcher)
  • Admin API changes take effect immediately
  • Dynamic routes (/api/v1/cms/{*path}) handle content types added after startup

Migration Strategy

The system uses a safe, additive migration approach:

New Content Type

CREATE TABLE IF NOT EXISTS {table} (
  id TEXT PRIMARY KEY,
  {field_columns},
  {protocol_columns}
)

New Fields Added

When the schema gains new fields, the system compares expected columns vs actual and generates:

ALTER TABLE {table} ADD COLUMN {column} {type}

Only additive changes are applied automatically.

Type Changes

If a field type changes (e.g., textinteger), the system detects the mismatch and generates a rebuild migration script saved to migrations/manual/:

  • SQLite: Uses PRAGMA foreign_keys=OFF, creates new table, copies data, drops old, renames
  • PostgreSQL: Wraps in a transaction with BEGIN / COMMIT
  • MySQL: Uses SET FOREIGN_KEY_CHECKS=0

These scripts require manual review and execution.

What Never Happens

  • Columns are never dropped — removing a field from TOML does not drop the column
  • Data is never modified — existing values remain untouched
  • Tables are never dropped — deleting a schema preserves the table

Indexes

Unique indexes are auto-created for:

  • Fields with unique = true
  • Explicit [[indexes]] definitions
[[indexes]]
fields = ["slug"]
unique = true

[[indexes]]
fields = ["status", "created_at"]

Junction Tables

For many_to_many and many_way relations, junction tables are auto-created:

CREATE TABLE IF NOT EXISTS {source}_{target} (
  {source_singular}_id TEXT NOT NULL,
  {target}_id TEXT NOT NULL,
  PRIMARY KEY ({source_singular}_id, {target}_id)
)

Protected Tables

System tables cannot be used as content type tables. These include: users, posts, pages, categories, tags, media, comments, products, orders, wallets, and all other built-in tables.

Reserved route segments (auth, admin, users, media, etc.) cannot be used as singular or plural names.

File Structure

extensions/
  content_types/
    course.toml          # Course content type
    instructor.toml      # Instructor content type
    lesson.toml          # Lesson content type
migrations/
  manual/                # Type-change rebuild scripts (for manual review)

On this page