⚡ fastgen-cli
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.
✨ Why fastgen-cli?
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
- 🧩 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
📦 Installation
# From crates.io (any platform with a Rust toolchain)
cargo install fastgen-cli
# Or download a prebuilt binary from GitHub Releases:
# https://github.com/YIbaikaishui/fastgen-cli/releases/latest
fastgen is a single self-contained binary — running it needs no Python runtime
at all. The projects it scaffolds are ordinary Python 3.11+ FastAPI apps.
🚀 Quick start
# Scaffold a whole project (src/ layout: .env, src/main.py, src/core/, tests/, Alembic)
fastgen new my-app
cd my-app && uv sync && uv run alembic upgrade head && uv run uvicorn src.main:app --reload
# Scaffold a user module (creates src/modules/user/ + src/core/ + registry + tests)
fastgen make module user
# See all registered modules and their boundaries
fastgen list
That'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. │
└────────┴──────────────────┴────────────────┘
🔧 How it works
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.
🧱 What it generates
fastgen new <name>
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
fastgen make module <feature>
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]:
...
🔁 Migrations (Alembic)
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 step
migrations/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.
⚖️ How does it compare?
vs. other FastAPI module generators & frameworks
| 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 |
Where fastgen stands out
- 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.
Honest trade-off
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.
vs. starting with uv init
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).
🛠️ CLI reference
| 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 |
Options
| 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 |
📐 Conventions (fixed)
- 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).
🔭 Roadmap
-
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) -
make resource— full CRUD router generation
🧑💻 Development
git clone https://github.com/YIbaikaishui/fastgen-cli.git
cd fastgen-cli
cargo build --release
cargo test
Lint / format: cargo clippy --all-targets and cargo fmt --check.
📄 License
MIT © 一白开水
Release files for fastgen-cli 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastgen_cli-0.7.0.tar.gz | 45.9 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| fastgen_cli-0.7.0-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| fastgen_cli-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64 | Details |
| fastgen_cli-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | Python 3 | none | Linux glibc 2.17+ ARM64 | Details |
| fastgen_cli-0.7.0-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
| fastgen_cli-0.7.0-py3-none-macosx_10_12_x86_64.whl | Python 3 | none | macOS 10.12+ x86-64 | Details |
Total release size: 6.5 MB
Release files / fastgen_cli-0.7.0.tar.gz
| Download URL | fastgen_cli-0.7.0.tar.gz |
|---|---|
| Size | 45.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
71a9dd6c67c89b92397baf9e863bef4f88924160faa68099075af0a7e0b48102
|
|
BLAKE2b-256 checksum How to use checksums |
db9ed4e46ddee32208fdaff767bf2ca93966cdd0070d28ac2badcc265628d0c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / fastgen_cli-0.7.0-py3-none-win_amd64.whl
| Download URL | fastgen_cli-0.7.0-py3-none-win_amd64.whl |
|---|---|
| Size | 1.7 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
2268d718fad1488e8c4ee8eccad014f5e357c0548d78e689e266c6a59404ff70
|
|
BLAKE2b-256 checksum How to use checksums |
5fb183f86b5c2e9c9bfbf914a263a6c12d8939453c5cf74945c78cab0709649f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / fastgen_cli-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | fastgen_cli-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | Linux glibc 2.17+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
e645983f81b8df81af42258176b9d612aa50902d185fba5244c276d17a393043
|
|
BLAKE2b-256 checksum How to use checksums |
450a80179c371b1a164a423a325747b6813c573c55320c5dcbdea5d319e25181
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / fastgen_cli-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | fastgen_cli-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Linux glibc 2.17+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
acff990cc9a712579370c8445ee1b6af0e2006c0b83c1a5a019a9e645c140ee8
|
|
BLAKE2b-256 checksum How to use checksums |
76072bcaab77cf4c9ee79c9c30b6242fd3e2495126391f4bf210122ccd9f2dab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / fastgen_cli-0.7.0-py3-none-macosx_11_0_arm64.whl
| Download URL | fastgen_cli-0.7.0-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.1 MB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
64c99f124fd9f95728848bf4a4b6b1b10edb2cbb7d18874f222d5c86e5732d00
|
|
BLAKE2b-256 checksum How to use checksums |
3099830f220da439ca3cdd8c5f0e9a58a1baeda94e1da07e208f923532edcef2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / fastgen_cli-0.7.0-py3-none-macosx_10_12_x86_64.whl
| Download URL | fastgen_cli-0.7.0-py3-none-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Python 3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
27cb0564a75a56d21d0b312148bdc5cf6115eab97d36de04b38dbc05978dd174
|
|
BLAKE2b-256 checksum How to use checksums |
5932763f5dac339e4c63a5133bda27bedb141b4d220e732418a5218e5357daf1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|