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
| Capability | API |
|---|---|
| Add API routes | ctx.include_router(router, prefix="/my-plugin") |
| React to record events | ctx.on_record_create("posts", handler) |
| Add Admin UI sidebar pages | ctx.add_admin_page(...) |
| Read/write plugin settings | ctx.get_setting("key") — stored in DB |
| Define database models | Inherit from Base — auto-tracked by Alembic |
Design Principles
- Zero core modification — plugins never touch
app/files - Drop-in discovery — copy a folder, restart, done
- Isolated namespace — routes under
/api/v1/plugins/{prefix}/ - Graceful failure — a broken plugin logs an error and is skipped; the server starts normally
- Settings in DB — plugin config lives in the
plugin_settingstable, 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 pagesMinimal 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
- Startup — FastCMS calls
load_plugins("./plugins", app, plugin_settings) - Discovery — scans
plugins/for sub-directories with__init__.py - Validation — checks
PLUGIN_METAhasid,name,version - Context creation — builds
PluginContext(plugin_id, app, settings) - Registration — calls
register(ctx)— routes/hooks/pages registered here - Running — plugin routes are live, hooks fire on events
- 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__.pySecurity
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_authorrequire_admindependencies - Review third-party plugins before installing
Troubleshooting
| Problem | Solution |
|---|---|
| Plugin not discovered | Ensure plugins/my_plugin/__init__.py exists and defines PLUGIN_META + register() |
PLUGIN_META missing keys | Required keys: id, name, version |
register() error | Check server logs — the error is printed and the plugin is skipped |
| Routes return 404 | Confirm ctx.include_router(router, prefix="/my-plugin") was called |
| Settings not saving | Use PATCH /api/v1/admin/plugins/{id}/settings endpoint |
Metrics & Monitoring
Built-in request counters, latency histograms, and error rates. Export to Prometheus for integration with Grafana and alerting systems.
Plugin Development Guide
Step-by-step guide to building FastCMS plugins — API routes, database models, event hooks, Admin UI pages, and persistent settings.