🚧 FastCMS is under active development — not ready for production use. APIs and features may change without notice.
FastCMS
Plugins

Plugin System

Extend FastCMS with Python plugin packages that add API routes, event hooks, Admin UI pages, database models, and persistent settings — all without modifying core files.

Plugin System

FastCMS includes a production-ready plugin system inspired by PocketBase's extension model. Plugins are plain Python packages you drop into the plugins/ directory. On startup, FastCMS discovers them automatically and calls their register() function — giving each plugin a PluginContext to hook into the system.

What Plugins Can Do

CapabilityAPI
Add API routesctx.include_router(router, prefix="/my-plugin")
React to record eventsctx.on_record_create("posts", handler)
Add Admin UI sidebar pagesctx.add_admin_page(...)
Read/write plugin settingsctx.get_setting("key") — stored in DB
Define database modelsInherit from Base — auto-tracked by Alembic

Design Principles

  1. Zero core modification — plugins never touch app/ files
  2. Drop-in discovery — copy a folder, restart, done
  3. Isolated namespace — routes under /api/v1/plugins/{prefix}/
  4. Graceful failure — a broken plugin logs an error and is skipped; the server starts normally
  5. Settings in DB — plugin config lives in the plugin_settings table, editable via Admin UI

Plugin Directory Structure

The minimal plugin is a single file. A full-featured plugin looks like:

plugins/
└── my_plugin/
    ├── __init__.py     # REQUIRED: PLUGIN_META dict + register(ctx) function
    ├── routes.py       # Optional: FastAPI APIRouter
    ├── models.py       # Optional: SQLAlchemy models (inherit from Base)
    ├── hooks.py        # Optional: async event handlers
    └── templates/      # Optional: Jinja2 templates for admin pages

Minimal Plugin

# plugins/hello_world/__init__.py
PLUGIN_META = {
    "id": "hello-world",
    "name": "Hello World",
    "version": "1.0.0",
    "author": "Your Name",
    "description": "A minimal example plugin",
}

def register(ctx) -> None:
    from fastapi import APIRouter
    router = APIRouter()

    @router.get("/hello")
    async def hello():
        return {"message": "Hello from Hello World plugin!"}

    ctx.include_router(router, prefix="/hello-world")

Restart FastCMS and call:

curl http://localhost:8000/api/v1/plugins/hello-world/hello
# → {"message": "Hello from Hello World plugin!"}

Plugin Lifecycle

  1. Startup — FastCMS calls load_plugins("./plugins", app, plugin_settings)
  2. Discovery — scans plugins/ for sub-directories with __init__.py
  3. Validation — checks PLUGIN_META has id, name, version
  4. Context creation — builds PluginContext(plugin_id, app, settings)
  5. Registration — calls register(ctx) — routes/hooks/pages registered here
  6. Running — plugin routes are live, hooks fire on events
  7. Admin listing — plugin appears at /admin/plugins

Admin UI

Loaded plugins are listed at /admin/plugins with name, version, author, and a settings link.

Plugins can also add their own pages to the sidebar:

ctx.add_admin_page(
    nav_id="hello-world",
    label="Hello World",
    icon="fa-star",
    url="/admin/plugins/hello-world",
)

Plugin Settings

Plugin settings are stored in the plugin_settings database table (key/value per plugin).

Read a setting at startup:

def register(ctx) -> None:
    api_key = ctx.get_setting("api_key", default="")

Update settings via API:

PATCH /api/v1/admin/plugins/hello-world/settings
Authorization: Bearer ADMIN_TOKEN
{"api_key": "sk-..."}

Example Plugin

A complete reference implementation lives at plugins/example_plugin/. It demonstrates routes, event hooks, and admin page registration. Copy it as your starting point:

cp -r plugins/example_plugin plugins/my_plugin
# Edit plugins/my_plugin/__init__.py

Security

Plugins run inside the FastCMS process with full Python access. Only install plugins from sources you trust.

  • Never expose raw SQL through plugin routes — use SQLAlchemy ORM
  • Always check authentication via require_auth or require_admin dependencies
  • Review third-party plugins before installing

Troubleshooting

ProblemSolution
Plugin not discoveredEnsure plugins/my_plugin/__init__.py exists and defines PLUGIN_META + register()
PLUGIN_META missing keysRequired keys: id, name, version
register() errorCheck server logs — the error is printed and the plugin is skipped
Routes return 404Confirm ctx.include_router(router, prefix="/my-plugin") was called
Settings not savingUse PATCH /api/v1/admin/plugins/{id}/settings endpoint

Build your first plugin →

On this page