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 is AI-native — but it doesn't bundle AI into the core wheel. Instead, RAG, vector search, autonomous agents, and other extensions ship as plugins you install on demand. The roadmap doubles down on this: future versions go deeper on agent orchestration and retrieval, all shipped as opt-in plugins, never bloating the install.
This means: pick the AI capabilities you need, skip the ones you don't, keep your wheel small. Same model for anything you'd build yourself.
Installing a pre-built plugin
# inside any FastCMS project directory
fastcms plugin install ai-core
fastcms plugin install ai-vectors
fastcms plugin install ai-rag
fastcms plugin install ai-agentsThe CLI fetches the plugin from the fastcms-plugins repository, resolves dependencies, and places it in your project's plugins/ directory.
Restart FastCMS (Ctrl+C, then fastcms dev again) — plugins load once at startup. List installed plugins:
fastcms plugin listThe official plugin registry currently includes:
| Plugin | What it adds | Depends on |
|---|---|---|
ai-core | LLM provider abstraction (OpenAI, Anthropic, Ollama) | — |
ai-vectors | Embedding storage + semantic search | ai-core |
ai-rag | Retrieval-augmented Q&A on uploaded documents | ai-core, ai-vectors |
ai-agents | Autonomous agents with FastCMS data tools | ai-core |
example | Reference plugin showing routes, hooks, admin UI | — |
Dependencies are installed automatically (ai-rag pulls in ai-vectors and ai-core for you).
How the plugin system works
Plugins are plain Python packages dropped 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. Inspired by PocketBase's extension model.
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.