A nest-cli style module manager for FastAPI — scaffold modules, keep the project tidy, and let AI agents see the whole structure at a glance.
Zero-config. One command. Fill in the business logic yourself.
FastAPI is famously unopinionated — which is great for freedom, but bad for structure. Projects drift into chaos: routers scattered, entities everywhere, no one knows what modules exist.
fastgen-cli fixes exactly that. It manages module structure, not your business code:
- 🏗️ Scaffold whole projects —
fastgen new my-appcreates a best-practicesrc/-layout FastAPI project (.env,src/main.py,src/core/, module registry,tests/, Alembic migrations) ready to run - 🗂️ One module = one folder (
<src>/modules/<feature>/), with a consistent shape every time - ⚡ Rust binary — one self-contained executable, starts instantly, no Python runtime needed to run the tool
- 🤖 AI-native —
fastgenowns the structure,codex/opencodewrite the code inside it - 🧩 Minimal skeleton — ORM model, schemas, service boundary, router + shared session dependency. Just enough to see the module, never enough to get in the way
- 📇 Auto-maintained registry —
<src>/modules/__init__.pymaps every module to its import path; AI agents and devs read it to understand the project instantly - 🔌 Shared DB core generated once —
<src>/core/with pydantic-settings config + async SQLAlchemyget_session(best-practice,expire_on_commit=False,AsyncAttrs) - 🔁 Alembic migrations out of the box —
alembic upgrade headevolves your schema instead of deletingapp.db; autogenerate picks up model changes automatically - 🛡️ Never overwrites your code — only generates what's missing or empty
You usually don't need to install anything. uvx runs fastgen on demand:
uvx fastgen-cli make module orderIf you use it daily, install it permanently:
uv tool install fastgen-cli # or: pip install fastgen-cli / uv add fastgen-clifastgen is a single self-contained Rust binary shipped inside the wheel (the
uv model) — the tool itself needs no Python runtime, and it lands on your
PATH as fastgen. Wheels are published for Linux (x86_64 / aarch64,
glibc 2.17+), macOS (Intel / Apple Silicon) and Windows.
Alternative installs:
cargo install fastgen-cli # from crates.io (needs a Rust toolchain)
# or download a prebuilt binary from GitHub Releases:
# https://github.com/YIbaikaishui/fastgen-cli/releases/latestThe projects it scaffolds are ordinary Python 3.11+ FastAPI apps.
Fastest — start from the template (nothing to install, just clone): fastapi-fastgen-starter — a working FastAPI project with a filled-in example module. Click Use this template on GitHub, or:
git clone https://github.com/YIbaikaishui/fastapi-fastgen-starter my-app
cd my-app && uv sync && uv run alembic upgrade head && uv run uvicorn src.main:app --reloadOr scaffold from scratch anywhere, with zero install (uvx runs fastgen
without installing anything):
uvx fastgen-cli new my-app
cd my-app && uv sync && uv run alembic upgrade head && uv run uvicorn src.main:app --reloadAdd a module whenever — one command, main.py never touched:
uvx fastgen-cli make module orderSee the whole structure — for you and for your AI agent:
uvx fastgen-cli listThat's it. No config file, no YAML, no spec — run a command, get the skeleton:
$ fastgen list
Registered modules
┏━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃ module ┃ path ┃ description ┃
┡━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│ user │ src.modules.user │ User module. │
└────────┴──────────────────┴────────────────┘
Every fastgen make module <feature> command does four things, in order:
- Scaffold — render the module skeleton into
<src>/modules/<feature>/(model / schemas / service / router / tests). It only ever writes missing or empty files; anything already there stays untouched. - Register — add the module to the auto-maintained registry (
<src>/modules/__init__.py), a plainmodules: dict[str, str]mapping each module name to its import path. - Auto-mount — idempotently sync
<src>/main.pyso it imports the registry and callsapp.include_router(module.router)for each entry, guarded by a# --- fastgen: auto-mount (do not remove) ---marker. No hand-editingmain.pywhen adding modules. - Verify —
fastgen listprints the registry as a table, so both you and AI agents see the whole module structure at a glance.
fastgen new <name>is the same mechanism applied to a whole project: it scaffoldssrc/core/, the registry,tests/, Alembic migrations and.fastgen.json, then letsmake modulegrow the app from there.
The registry is deliberately boring — one plain dict, no metadata, no framework:
# src/modules/__init__.py — auto-maintained by fastgen
modules: dict[str, str] = {
"user": "src.modules.user",
}
__all__ = ["modules"]Because nothing generated by fastgen depends on fastgen at runtime, you can drop the tool anytime and keep a completely ordinary FastAPI project.
By default fastgen is a pure deterministic scaffolder — no agent, no API keys,
nothing to install beyond uv. If you want an agent to fill the scaffold with
real code, opt in with --ai:
npm install -g @openai/codex # or: curl -fsSL https://opencode.ai/install | bash
fastgen new myapp --ai "task manager API with projects and tasks"
fastgen make module order --ai "line items, status enum, total calculation"How it works:
- fastgen renders the deterministic scaffold (instant, byte-stable)
- the agent (
codexpreferred,opencodeas fallback; force one with--agent) fills it in, guided by a prompt that encodes fastgen's conventions - fastgen verifies and reconciles: reports the files the agent touched, auto-registers any module it created, re-syncs the auto-mount block, and syntax-checks the result
Use --agent codex|opencode to pick a specific agent, or point fastgen at any
other CLI agent with FASTGEN_AGENT_CMD (e.g. FASTGEN_AGENT_CMD="claude -p").
--dry-run previews the scaffold without running an agent.
A complete, runnable best-practice FastAPI project:
my-app/
├── .env / .env.example # DATABASE_URL etc.
├── .gitignore # ignores .env, venv, __pycache__, *.db
├── .python-version # 3.11
├── pyproject.toml # deps + ruff / pytest config
├── README.md
├── .fastgen.json # {"source_dir": "src"} — layout used by fastgen
├── src/
│ ├── __init__.py
│ ├── main.py # FastAPI app; module routers auto-load from the registry
│ ├── core/ # shared infra (never overwritten)
│ │ ├── __init__.py
│ │ ├── config.py # pydantic-settings Settings, reads .env
│ │ └── database.py # Base (AsyncAttrs), async engine, get_session
│ └── modules/
│ └── __init__.py # 📇 module registry (auto-maintained)
├── migrations/ # Alembic migrations (alembic.ini at project root)
│ ├── env.py # async env; DATABASE_URL from settings, models from the registry
│ ├── script.py.mako
│ └── versions/
│ └── 0001_initial.py # empty baseline revision
└── tests/
├── __init__.py
├── conftest.py # httpx ASGI client fixture
└── test_health.py # /health smoke test
src/ (or app/ for a legacy project; fastgen auto-detects the layout)
├── core/ # auto-created on first use (never overwritten)
│ ├── __init__.py
│ ├── config.py # pydantic-settings Settings, DATABASE_URL from .env
│ └── database.py # Base (AsyncAttrs), async engine, get_session
└── modules/ # 📇 vertical slices: one folder per business domain
├── __init__.py # module registry (auto-maintained)
└── user/ # each module is internally layered
├── __init__.py # re-exports the router from .api.router
├── domain/ # entities + repository port (no I/O or framework)
│ ├── model.py # SQLAlchemy entity on Base (__tablename__ = plural)
│ └── repository.py # UserRepository Protocol (add/get/list/delete)
├── application/ # use cases + DTOs (free of HTTP)
│ ├── schemas.py # UserBase / UserCreate / UserUpdate / UserRead
│ │ # UserRead has from_attributes=True so ORM objects serialize
│ └── user_service.py # UserService (constructor-injected repo) + UserError hierarchy
├── infrastructure/ # SQLAlchemy adapter for the repository port
│ └── user_repository.py
├── api/ # FastAPI layer: SessionDep + router, maps exceptions to HTTP
│ └── router.py # APIRouter (prefix="/users")
└── tests/ # in-memory SQLite test DB + get_session override
├── conftest.py
└── test_user.py
Routers are auto-mounted: fastgen make module idempotently syncs main.py
to import registered modules from the registry and app.include_router(...) each —
no hand-editing main.py when adding a module (guarded by a fastgen: auto-mount marker).
fastgen make module scaffolds the full vertical-slice skeleton above; every layer
already wires the shared session dependency, so you just add endpoints and business
logic. The generated router.py looks like:
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from src.core.database import get_session
from src.modules.user.application.user_service import UserNotFound, UserService
from src.modules.user.domain.model import User
from src.modules.user.infrastructure.user_repository import SqlUserRepository
SessionDep = Annotated[AsyncSession, Depends(get_session)]
router = APIRouter(prefix="/users", tags=["users"])
def _service(session: AsyncSession) -> UserService:
return UserService.from_repository(SqlUserRepository(session))
@router.get("", response_model=list[User])
async def list_users(session: SessionDep) -> list[User]:
...fastgen new ships Alembic scaffolding (alembic.ini + migrations/) wired to your
settings and models — schema is managed by migrations, not create_all at startup, so
you evolve the dev DB instead of deleting app.db.
uv run alembic upgrade head # apply all pending migrations (baseline included)
uv run alembic revision --autogenerate -m "add user email" # diff models -> new migration
uv run alembic upgrade head # apply it
uv run alembic downgrade -1 # roll back one stepmigrations/env.pyimports every registered module'smodelso autogenerate sees all tables.- Adding Alembic to an existing project:
fastgen init alembicwrites the scaffolding idempotently (never overwrites). If the DB was already created viacreate_all, adopt it withuv run alembic stamp head, or drop the dev DB and re-create viaupgrade head.
| Tool | What it is | Runtime dependency you must keep | Generated module |
|---|---|---|---|
| fastgen-cli | Generator only — plain FastAPI (Rust CLI) | None | Model / schemas / service / router + tests + auto-maintained registry; Alembic migrations |
| PyNest | Framework on FastAPI (NestJS-style) | pynest-api (nest.core) |
Module with @Module / @Controller / @Injectable, DI container |
| FastKit | Meta-framework + CLI (Laravel-style) | fastkit-core |
Full CRUD module (model / schema / repository / service / router) |
| Gondola | CLI with Rails-like conventions | gondola-cli + default PostgreSQL stack |
Models / routers / services / mailers / tests, Alembic migrations |
| FastStack | Full framework (Django-like) | faststack-frame |
App module (models / routes / schemas / services / admin) |
| RapidKit | Module engine + CLI (FastAPI & NestJS) | rapidkit-core + npx/poetry toolchain |
Kits (fastapi.standard / fastapi.ddd) + installable module catalog |
- Zero runtime lock-in. Everything fastgen generates is plain Python on top of vanilla FastAPI + SQLAlchemy — nothing requires
fastgenat runtime. The others all ship their own framework/runtime that your project keeps depending on. - No new concepts to learn. No
@Module/@Injectabledecorators, no DI container, no repository base classes, no workspace metadata. The skeleton uses idioms you already know (SessionDep = Annotated[AsyncSession, Depends(get_session)]). - Incremental, not all-or-nothing.
fastgen make modulegrows an existing project (src/orapp/layout) instead of forcing you to start inside a framework — you can adopt it on top of any FastAPI project, including the ones above. - AI/agent-friendly. An auto-maintained registry (
src/modules/__init__.py) plusfastgen listmeans both humans and AI agents see the whole module structure at a glance. - Never overwrites.
src/core/is only generated when missing or empty.
The others generate more for you: FastKit's full CRUD router, Gondola's mailers, PyNest's dependency injection for complex enterprise apps, RapidKit's module upgrade/rollback lifecycle. Choose them when you want those batteries and can accept their runtime and conventions. Choose fastgen when you want a lean, standard, zero-coupling base that you shape yourself.
Contract-first generators (
fastapi-code-generator, OpenAPI Generatorpython-fastapi) are a different category: they turn an OpenAPI spec into code and complement fastgen when your spec is the source of truth.
uv init my-app is the natural baseline — minimal, universal, no lock-in. The trade-offs:
uv init |
fastgen new |
|
|---|---|---|
| What you get | pyproject.toml + main.py hello world |
Complete FastAPI app: .env, src/main.py (lifespan + /health), src/core/ (pydantic-settings + async SQLAlchemy), module registry, tests/, Alembic migrations, ruff/pytest config |
| Then you must | Add deps, build the src/ layout, write lifespan/config/DB/tests by hand |
Add your business logic |
| Resulting structure | Differs per developer | Identical across projects |
| Module management later | None | fastgen make module keeps a registry you can fastgen list |
| Lock-in | None | Layout is plain files; drop fastgen anytime, nothing generated forces it |
Pros of uv init: universal, minimal, zero opinion, and you already have uv installed.
Cons: every FastAPI-specific decision (layout, DB session wiring, config, tests) is left to you, so each project ends up structured differently.
Pros of fastgen new: one command yields a complete best-practice base; consistent across the whole org; modules stay discoverable via the registry; never overwrites your code; easy for AI agents to reason about.
Cons: opinionated layout (src/ + core/ + registry) — if you need a non-standard structure you adapt it yourself; FastAPI-only.
They're complementary, not competing: a fastgen new project is still managed by uv (uv sync, uv run). And if you did start from uv init, you can adopt fastgen later — run fastgen make module <feature> in the project and it creates core/, modules/ and the registry for you (it auto-detects the layout).
| Command | Description |
|---|---|
fastgen new <name> |
Scaffold a new best-practice src/-layout FastAPI project (core + registry + tests + Alembic) |
fastgen make module <feature> |
Scaffold a feature module (model / schemas / service / router / tests), auto-mount its router, register it |
fastgen init alembic |
Add Alembic migration scaffolding to an existing project (idempotent) |
fastgen list |
List registered modules, import paths, and purposes |
fastgen --version / -V |
Show version |
| Flag | Applies to | Description |
|---|---|---|
--dir <path> / -d |
new, make module, init alembic, list |
Target project root (default: current dir) |
--title <name> |
new |
Human-readable app title (defaults to the project name) |
--description <text> |
new |
Short project description |
--dry-run |
new, make module, init alembic |
Preview files without writing anything |
--force / -f |
new, make module |
Overwrite existing files |
- Layout —
fastgen newcreates asrc/layout and records it in.fastgen.json.fastgenresolvessrcfrom.fastgen.json, then by auto-detection, and finally falls back toapp/for existing projects. - Modules live in
<src>/modules/<feature>/— one business unit per folder, scaffolded as a vertical slice:domain/(model.py+repository.pyport),application/(schemas.pyXBase/XCreate/XUpdate/XReadwithfrom_attributesonXRead, plus<feature>_service.py),infrastructure/(Sql*Repository),api/(router.py), andtests/. - Router exposes
prefix="/<plural>"(REST-style), reusesSessionDepfrom<src>.core.database, is auto-mounted intomain.pyfrom the registry (guarded by afastgen: auto-mountmarker — don't remove it), and maps domain exceptions toHTTPException. - Registry —
<src>/modules/__init__.pymaps module name → import path. Always kept in sync by fastgen; don't hand-edit. - Core —
<src>/core/config.pyanddatabase.pyare generated only when missing or empty. Existing code is never touched, even with--force. - Schema — managed by Alembic migrations (not
create_allat startup).
-
new— scaffold a whole best-practicesrc/-layout project -
make module— model / schemas / service / router / tests + auto-mount - Module registry +
fastgen list - Alembic migrations (
init alembic, autogenerate, upgrade) - AI agent integration —
codex/opencodefill the scaffolds (--ai), fastgen verifies and reconciles -
make resource— full CRUD router generation
git clone https://github.com/YIbaikaishui/fastgen-cli.git
cd fastgen-cli
cargo build --release
cargo testLint / format: cargo clippy --all-targets and cargo fmt --check.
Publish wheels to PyPI (uv model — maturin + ziglang for portable,
low-glibc wheels):
maturin build --release --zig --target <target> # one per platform
maturin sdist
maturin publishMIT © 一白开水