Skip to main content

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. Base alone 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.prefix only. 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_key from an environment variable, never hardcode it
  • Change initial_admin_password before first deploy
  • Serve behind HTTPS (nginx / Caddy / cloud load balancer)
  • Set allow_delete=False if deletes aren't needed in production
  • Use models_exclude to hide sensitive internal tables
  • Restrict /admin access 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

Metadata

Release files for auto-fastapi-admin-panel 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for auto-fastapi-admin-panel 0.1.3
File Size Uploaded Provenance
auto_fastapi_admin_panel-0.1.3.tar.gz 71.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for auto-fastapi-admin-panel 0.1.3
File Interpreter ABI Platform
auto_fastapi_admin_panel-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 202.0 kB

Release files / auto_fastapi_admin_panel-0.1.3.tar.gz

Download URL auto_fastapi_admin_panel-0.1.3.tar.gz
Size 71.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a087e960312c7375960fc12b2c45c1a16a975be41b33386aad6af77cc4953e63
BLAKE2b-256 checksum
How to use checksums
841cddc9c9119c59fa3ccb4309afc3accc11e3027279a0538b2fb11fa3476d34
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.0

Release files / auto_fastapi_admin_panel-0.1.3-py3-none-any.whl

Download URL auto_fastapi_admin_panel-0.1.3-py3-none-any.whl
Size 130.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
adefee1bca3baa956331bd1e0e968453c0c727a87fb5ad2101399d445a09e066
BLAKE2b-256 checksum
How to use checksums
29fc126a1bfc3bb28103e782bc960ed3b6291946928353a6dcce50ea50a83de2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.0

Security for auto-fastapi-admin-panel 0.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page