A developer debug toolbar for FastAPI — inspect SQL, requests, performance, logs, and cache in real time.
Project description
FastPanel
A developer debug toolbar for FastAPI — see every SQL query, request detail, log record, and cache operation in a floating in-browser panel, with zero code changes to your routes.
Inspired by django-debug-toolbar, built for the modern async FastAPI stack.
Features
| Panel | What it shows |
|---|---|
| SQL | Every SQLAlchemy query: timing, formatted SQL, slow query highlighting (>100ms), calling location |
| Request | Method, URL, path/query params, headers, cookies, JSON body |
| Response | Status code, headers, content type, size |
| Performance | Total wall time, CPU time, panel overhead |
| Logging | All Python logging records at WARNING+ during the request |
| Cache | Hit/miss/set/delete events, hit rate, per-operation log |
| Headers | Full request and response header tables |
- Zero overhead in production (
enabled=Falseis a pure pass-through) - Two-line mount — no route decorators, no config files
- Async-native — works with
asyncio,asyncpg,aiosqlite - Pluggable custom panels via
AbstractPanel - No frontend build step — vanilla HTML/CSS/JS
Installation
pip install fastpanel
# With SQLAlchemy support (SQL panel):
pip install fastpanel[sqlalchemy]
# With Redis support (Cache panel):
pip install fastpanel[redis]
# Everything:
pip install fastpanel[all]
Or with Poetry:
poetry add fastpanel
poetry add fastpanel[sqlalchemy]
Quickstart
from fastapi import FastAPI
from fastpanel import FastPanel
import os
app = FastAPI()
# Your routes here...
# Mount FastPanel — two lines, that's it.
FastPanel(app, enabled=os.getenv("ENVIRONMENT") == "development")
Start your app and visit any HTML page. The toolbar appears in the bottom-right corner.
Tip: Set the
FASTPANEL_ENABLED=trueenvironment variable instead of hardcodingenabled=True. That way it's impossible to accidentally enable it in production.
Configuration
All settings can be passed to the FastPanel constructor or set via environment
variables (FASTPANEL_ prefix):
| Setting | Env var | Default | Description |
|---|---|---|---|
enabled |
FASTPANEL_ENABLED |
False |
Master switch. Never True in production. |
mount_path |
FASTPANEL_MOUNT_PATH |
/__fastpanel |
URL prefix for internal routes |
store_max_requests |
FASTPANEL_STORE_MAX_REQUESTS |
100 |
Max requests in memory (LRU) |
show_sql |
FASTPANEL_SHOW_SQL |
True |
Enable SQL panel |
show_logging |
FASTPANEL_SHOW_LOGGING |
True |
Enable Logging panel |
show_cache |
FASTPANEL_SHOW_CACHE |
True |
Enable Cache panel |
slow_query_ms |
FASTPANEL_SLOW_QUERY_MS |
100.0 |
SQL query slow threshold (ms) |
excluded_paths |
— | [] |
URL prefixes to skip (mount_path always excluded) |
extra_panels |
— | [] |
Custom panel classes to append |
FastPanel(
app,
enabled=True,
slow_query_ms=50.0,
store_max_requests=200,
excluded_paths=["/health", "/metrics"],
)
Cache Panel — CacheTracker
The Cache panel requires wrapping your cache client:
from fastpanel.panels.cache import CacheTracker, InMemoryCache
import redis.asyncio
# In-memory cache (development/testing):
cache = CacheTracker(InMemoryCache())
# Redis (production-like):
raw_redis = redis.asyncio.Redis.from_url("redis://localhost")
cache = CacheTracker(raw_redis)
# Use cache normally — all operations are tracked:
await cache.set("user:1", user_data)
value = await cache.get("user:1") # recorded as a hit
await cache.delete("user:1")
SQLAlchemy Integration
The SQL panel hooks into SQLAlchemy's global event system automatically — no changes needed to your engine or session setup. Works with:
AsyncEngine+AsyncSession(SQLAlchemy 2.x)create_async_engine("sqlite+aiosqlite://...")create_async_engine("postgresql+asyncpg://...")- Synchronous engines too
Note on async location tracking: With SQLAlchemy async engines, the query source location (
app/models.py:42) may show<unknown>. This is because async SQLAlchemy uses greenlets to run sync drivers, and the async call stack isn't visible from within the cursor execute event. This is a known limitation. See DEVLOG.md Step 8 for details.
Writing a Custom Panel
Subclass AbstractPanel:
from fastpanel.panels.base import AbstractPanel
from starlette.requests import Request
from starlette.responses import Response
from typing import Any
class TimingPanel(AbstractPanel):
panel_id = "timing"
title = "Timing"
def __init__(self) -> None:
super().__init__()
self._events: list[str] = []
def reset(self) -> None:
self._events = []
async def process_request(self, request: Request) -> None:
self._events.append(f"Request received: {request.url.path}")
def get_stats(self) -> str:
return str(len(self._events))
def get_data(self) -> dict[str, Any]:
return {"events": self._events}
Register it:
FastPanel(app, enabled=True, extra_panels=[TimingPanel])
⚠️ Security Warning
FastPanel must never be enabled in production.
The toolbar exposes internal request data (headers, SQL queries, log records)
via the /__fastpanel/api/ endpoint. While the endpoint requires a UUID4
request_id (not guessable), it is still sensitive data that should never be
exposed in a production environment.
Recommended pattern:
import os
FastPanel(
app,
enabled=os.getenv("ENVIRONMENT") == "development"
# or:
# enabled=os.getenv("FASTPANEL_ENABLED", "false").lower() == "true"
)
Ensure ENVIRONMENT=development (or FASTPANEL_ENABLED=true) is never set in
your production environment or deployment configuration.
When enabled=False:
- All
/__fastpanel/routes return404(not403— the 404 does not reveal that FastPanel is installed) - The middleware is a 4-line pass-through with zero overhead
- No panel data is collected or stored
How This Was Built
See DEVLOG.md for the full step-by-step build narrative — from
pyproject.toml to the final test run. Every design decision, architectural
trade-off, and gotcha is documented.
Roadmap
- WebSocket panel
- Async log streaming (live panel updates)
- Redis store backend (persist panel data across restarts)
- Tortoise-ORM support
- Browser extension version (persistent across sessions)
- Profiler panel (line-level
cProfileintegration) - Template panel (Jinja2 render times)
- Custom panel plugin registry
Contributing
See CONTRIBUTING.md for development setup, testing instructions, branch naming conventions, and the PR checklist.
License
MIT — © 2026 officialalkenes
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastpanel-0.2.1.tar.gz.
File metadata
- Download URL: fastpanel-0.2.1.tar.gz
- Upload date:
- Size: 44.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.2 CPython/3.12.9 Darwin/25.3.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
795f8a03c730301bd6db1aa5b48764207006b6e42baf7c278c3f8cbcf4cbdccf
|
|
| MD5 |
d9c24184d3462b605a086556d09160a8
|
|
| BLAKE2b-256 |
0472fdb5cc02b2d932abc1d5acc974d8915d8f2879857eefb0c1d661c54d5bff
|
File details
Details for the file fastpanel-0.2.1-py3-none-any.whl.
File metadata
- Download URL: fastpanel-0.2.1-py3-none-any.whl
- Upload date:
- Size: 56.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.2 CPython/3.12.9 Darwin/25.3.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c06e4cf2de82f9a33879c7a0fb97b56881ca4f1fda39ddbb942989e9cd7bb06b
|
|
| MD5 |
8508770dfa3cb26002e672b725e13436
|
|
| BLAKE2b-256 |
b04d627cb7a89e2ed2a9e8715761d84d99cf5ead91cff468a88b8bbc0e80965e
|