Drop-in admin panel for FastAPI + SQLAlchemy projects. Auto-discovers your models via Alembic, provides full CRUD, JWT auth, and a dark React UI — integrated in 3 lines.
Project description
FastAPI Admin Panel
Auto-generated admin panel for FastAPI + SQLAlchemy + Alembic projects.
Self-funded hobby project — built by one developer in spare time, not backed by any company. Contributions, bug reports, and ideas are very welcome — see Contributing.
- Reads your
alembic/env.py→ discovers all SQLAlchemy models automatically - Full CRUD (list, search, sort, paginate, create, edit, delete)
- Bulk-select and bulk-delete rows
- DB-backed admin user authentication (JWT)
- Admin user management UI (create, edit, deactivate, change password, delete)
- Sync and async SQLAlchemy engine support
- React + TypeScript frontend with dark OLED theme
- 3 lines to integrate into any existing FastAPI project
Requirements
- Python 3.10+
- PostgreSQL or any SQLAlchemy-supported database
Installation
pip install auto-fastapi-admin-panel
The wheel ships with the pre-built frontend — no Node.js required.
Integration
There are three ways to add the panel, depending on how your project is structured.
Way 1 — Auto-discovery via Alembic (recommended)
If your project already uses Alembic, this is all you need. The panel reads
alembic.ini automatically to find your Base and all registered models.
# main.py
import os
from fastapi import FastAPI
from sqlalchemy import create_engine
from fastapi_admin_panel import AdminPanel, AdminConfig
# Import your model modules first so SQLAlchemy registers them
from myapp import models # noqa: F401
app = FastAPI()
engine = create_engine(os.environ["DATABASE_URL"])
config = AdminConfig(secret_key=os.environ["ADMIN_SECRET_KEY"])
panel = AdminPanel(app, engine, config=config)
The panel looks for alembic.ini next to main.py. All common layouts work:
script_location = alembic
script_location = database/migrations
script_location = %(here)s/database/migrations
If alembic.ini is in a non-standard location, tell the panel where to look:
from pathlib import Path
config = AdminConfig(
secret_key="...",
project_root=Path("/path/to/dir/containing/alembic.ini"),
)
Way 2 — Pass base= directly (no Alembic required)
If you don't use Alembic, or want to be explicit, pass your DeclarativeBase
class directly. The panel skips all file discovery.
# main.py
import os
from fastapi import FastAPI
from sqlalchemy import create_engine
from fastapi_admin_panel import AdminPanel, AdminConfig
from myapp.database import Base # your DeclarativeBase
from myapp import models # noqa: F401 — register all models
app = FastAPI()
engine = create_engine(os.environ["DATABASE_URL"])
config = AdminConfig(secret_key=os.environ["ADMIN_SECRET_KEY"])
panel = AdminPanel(app, engine, config=config, base=Base)
Important: Import your model modules before creating
AdminPanel.Basealone isn't enough — SQLAlchemy only knows about models that have been imported at least once.
Way 3 — Add panel table to your Alembic migrations
By default the panel creates its admin_panel_users table automatically at
startup (CREATE TABLE IF NOT EXISTS). If you prefer the table to be managed
by your own Alembic migrations (so it appears in alembic revision --autogenerate),
include the panel's metadata in your env.py:
# alembic/env.py
from myapp.database import Base
from fastapi_admin_panel import admin_metadata # ← add this
# Combine your metadata with the panel's metadata
target_metadata = [Base.metadata, admin_metadata]
Then generate and run migrations as usual:
alembic revision --autogenerate -m "add admin panel users table"
alembic upgrade head
When using this approach, the panel detects the table already exists and skips its auto-create step.
Async engine support
If you use an async engine (asyncpg, aiosqlite, etc.) the panel cannot
run its bootstrap at import time. Call await panel.bootstrap() inside your
lifespan:
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine
from fastapi_admin_panel import AdminPanel, AdminConfig
from myapp.database import Base
from myapp import models # noqa: F401
engine = create_async_engine(os.environ["DATABASE_URL"])
config = AdminConfig(
secret_key=os.environ["ADMIN_SECRET_KEY"],
title="My Project Admin",
prefix="/admin",
page_size=50,
allow_delete=True,
initial_admin_password=os.environ.get("ADMIN_PASSWORD", "changeme"),
)
@asynccontextmanager
async def lifespan(app: FastAPI):
await panel.bootstrap() # ← must be first; creates table + seeds admin user
yield
app = FastAPI(lifespan=lifespan)
panel = AdminPanel(app, engine, config=config, base=Base)
Dedicated admin module (clean architecture)
For larger projects, keep all admin-related setup in its own file so main.py
stays focused on your application logic.
admin.py — owns the engine, config, and panel singleton:
import os
from pathlib import Path
from sqlalchemy.ext.asyncio import create_async_engine
from fastapi_admin_panel import AdminPanel, AdminConfig
from myapp.database import Base
from myapp import models # noqa: F401
engine = create_async_engine(os.environ["DATABASE_URL"])
config = AdminConfig(
secret_key=os.environ.get("ADMIN_SECRET_KEY", "CHANGE-ME"),
title="My Project Admin",
prefix="/admin",
page_size=50,
allow_delete=True,
models_exclude=["AlembicVersion"],
token_expire_hours=8,
initial_admin_username="admin",
initial_admin_password=os.environ.get("ADMIN_PASSWORD", "changeme"),
project_root=Path(__file__).parent,
)
panel: AdminPanel | None = None
def init_admin(app) -> AdminPanel:
"""Register all admin routes and return the panel instance."""
global panel
panel = AdminPanel(app, engine, config=config)
return panel
main.py — imports and wires up the panel in the lifespan:
from contextlib import asynccontextmanager
from fastapi import FastAPI
from admin import init_admin, panel as _admin_panel
@asynccontextmanager
async def lifespan(app: FastAPI):
# 1. Register routes
init_admin(app)
# 2. Async DB bootstrap (creates table, seeds first admin user)
if _admin_panel is not None:
await _admin_panel.bootstrap()
# 3. Your own startup logic here
yield
# 4. Cleanup
app = FastAPI(title="My API", lifespan=lifespan)
@app.get("/")
async def root():
return {"status": "ok"}
Full working examples are in the examples/ directory.
Configuration reference
from fastapi_admin_panel import AdminConfig
from pathlib import Path
config = AdminConfig(
# ── Required ────────────────────────────────────────────────────────────
secret_key="...", # use: import secrets; secrets.token_hex(32)
# ── UI ──────────────────────────────────────────────────────────────────
title="Admin Panel", # shown in sidebar and browser tab
prefix="/admin", # URL prefix → http://host/admin
# ── Behaviour ───────────────────────────────────────────────────────────
page_size=50, # default rows per page
allow_delete=True, # set False to disable Delete globally
models_exclude=["AuditLog"], # model class names to hide from the panel
# ── Auth ────────────────────────────────────────────────────────────────
token_expire_hours=8,
initial_admin_username="admin",
initial_admin_password="admin", # change before production
# ── Discovery ───────────────────────────────────────────────────────────
project_root=Path(__file__).parent, # dir containing alembic.ini
)
All fields have defaults. The only one you should always set explicitly is
secret_key.
First login
On first startup an admin user is seeded automatically:
| Field | Default |
|---|---|
| Username | admin |
| Password | admin |
The seed runs once — only when admin_panel_users is empty. After that,
manage accounts through the panel UI at System → Admin Users, or reset
via SQL:
# Generate a bcrypt hash
python -c "from fastapi_admin_panel.auth.utils import hash_password; print(hash_password('newpass'))"
UPDATE admin_panel_users SET hashed_password = '<hash>' WHERE username = 'admin';
Admin Users page
The built-in System → Admin Users page lets you:
- Create new admin accounts (username + password)
- Edit username or toggle active/inactive
- Change any account's password
- Delete accounts
No DB access needed to manage panel users after initial setup.
Full example
import os
import secrets
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from sqlalchemy import create_engine
from myapp.models import Base
from myapp import models # noqa: F401
from fastapi_admin_panel import AdminPanel, AdminConfig
app = FastAPI(title="My API")
engine = create_engine(os.environ["DATABASE_URL"])
config = AdminConfig(
secret_key=os.environ.get("ADMIN_SECRET_KEY", secrets.token_hex(32)),
title="My Project Admin",
prefix="/admin",
page_size=25,
allow_delete=True,
models_exclude=["AlembicVersion"],
initial_admin_username="admin",
initial_admin_password=os.environ.get("ADMIN_PASSWORD", "changeme"),
token_expire_hours=8,
project_root=Path(__file__).parent,
)
panel = AdminPanel(app, engine, config=config)
@app.get("/")
def root():
return {"status": "ok"}
Performance impact in production
TL;DR: zero impact on your existing routes.
- Startup: model introspection is in-memory (iterating registered mappers), < 1 ms. Route registration for 50 models adds < 1 ms.
- Per request: admin routes live under
config.prefixonly. No global middleware is added. Your existing routes see zero overhead. - Database: no second connection pool. One short-lived connection per admin request, same pool as your app.
- Memory: the built frontend is ~200 KB (gzipped), loaded once at startup.
If you want to remove the admin surface from the internet entirely, block
/admin at the reverse-proxy level (nginx allow/deny by IP).
Development (from source)
git clone https://github.com/yourname/fastapi-admin-panel
cd fastapi-admin-panel
pip install -e ".[dev]"
python scripts/build.py # installs npm deps + builds frontend
# Hot-reload during development
# Terminal 1
uvicorn myapp.main:app --reload
# Terminal 2
cd frontend && npm run dev # Vite proxies /admin/api → :8000
Open http://localhost:5173/admin/.
Security checklist before production
- Set
secret_keyfrom an environment variable, never hardcode it - Change
initial_admin_passwordbefore first deploy - Serve behind HTTPS (nginx / Caddy / cloud load balancer)
- Set
allow_delete=Falseif deletes aren't needed in production - Use
models_excludeto hide sensitive internal tables - Restrict
/adminaccess by IP at the reverse-proxy level - Rotate the admin password after first login
Contributing
No company behind this — just spare-time development. All contributions welcome:
- Bug reports — open an issue with steps to reproduce
- Feature requests — open an issue describing the use case
- Pull requests — small focused changes are easiest to review
- Docs — typos, unclear wording, missing examples
git clone https://github.com/artaabbasi/fastapi-admin-panel
pip install -e ".[dev]"
python scripts/build.py
pytest
Project details
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 auto_fastapi_admin_panel-0.1.3.tar.gz.
File metadata
- Download URL: auto_fastapi_admin_panel-0.1.3.tar.gz
- Upload date:
- Size: 71.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a087e960312c7375960fc12b2c45c1a16a975be41b33386aad6af77cc4953e63
|
|
| MD5 |
210f2b139847e6b40e5a7dd253277494
|
|
| BLAKE2b-256 |
841cddc9c9119c59fa3ccb4309afc3accc11e3027279a0538b2fb11fa3476d34
|
File details
Details for the file auto_fastapi_admin_panel-0.1.3-py3-none-any.whl.
File metadata
- Download URL: auto_fastapi_admin_panel-0.1.3-py3-none-any.whl
- Upload date:
- Size: 130.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
adefee1bca3baa956331bd1e0e968453c0c727a87fb5ad2101399d445a09e066
|
|
| MD5 |
4992e2915a419096f783f2929073dd01
|
|
| BLAKE2b-256 |
29fc126a1bfc3bb28103e782bc960ed3b6291946928353a6dcce50ea50a83de2
|