A thin convention-and-scaffolding layer over FastAPI + SQLAlchemy — collapses the model/repository/service/schema/router module shape into one declaration.
Project description
mango
A thin convention-and-scaffolding layer over FastAPI + SQLAlchemy. mango
does not reimplement either — mango.Router IS fastapi.APIRouter,
mango.Schema IS a pydantic.BaseModel subclass — but a project built
with mango never needs import fastapi or import pydantic: routing,
schemas, dependency injection, the app instance, DB sessions, and error
handling all have a mango. name. Beyond that single front door, mango
removes the repetitive plumbing every FastAPI project rewrites: the
models.py / repository.py / service.py / schemas.py / router.py
/ __init__.py quintet a vertical-slice module normally needs, the
hand-maintained main.py router-mounting list, generic CRUD
repositories, DB session setup, and domain-exception-to-HTTP mapping.
Distribution name on PyPI is mango-api (pip install mango-api) since
mango/mango-framework were already taken; the import name is always
mango.
Full usage guide (for using mango in a new project): docs/GUIDE.md.
Project folder structure convention (mango init's layout, and why): docs/PROJECT_STRUCTURE.md.
Currently wired into the collabfluenz.backend reference project
(router mounting via MangoApp, BaseRepository built on
MangoRepository) — see that project's app/mango_registry.py and
app/main.py for a real-world example beyond examples/.
What's here
mango/web.py—Router,Depends,Query,Path,Body,Header,Cookie,Form,File,UploadFile,Request,Response,JSONResponse,status,HTTPException: the exact FastAPI classes, re-exported undermango.so route files neverimport fastapi.mango/schema.py—Schema, apydantic.BaseModelsubclass withfrom_attributes=Trueon by default (the setting every response schema in a DB-backed app needs but reliably forgets), plusField,field_validator,model_validatorre-exports.mango/module.py—@mango.moduledecorator +MangoModulebase class. Declare a module as one class; mango derives its public API from the class's attributes instead of a hand-written__init__.py.mango/repository.py—MangoRepository[Model], a generic async repository withget/add/update/delete/list/count/searchbuilt in, plusget_or_404,exists,add_many,delete_many,filter_by(**equals), andoptions=(SQLAlchemy loader options, for eager-loading relationships) onget()/list().mango/app.py—App(owns its own FastAPI instance internally, directly ASGI-callable, the default entry point for a new project) andMangoApp(wraps an existing FastAPI app, for incremental adoption). Both mount every registered module's router in dependency order (topological sort ondepends_on), raising a clearcircular module dependency: a -> b -> aerror instead of Python'sImportError ... most likely due to a circular importtraceback.Appalso wires in production-hardening middleware by default (security_headers=True; opt-inrate_limit=/cors_origins=) and exposesuse(plugin)+on_startup/on_shutdownhooks.mango/security.py—SecurityHeadersMiddleware(on by default) andRateLimitMiddleware(opt-in, in-memory sliding window — explicitly not a substitute for a real limiter in a horizontally-scaled deployment; see its docstring).mango/plugins.py—Pluginprotocol (install(app)) +App.use(plugin), the extension point for third-party/project-local code that doesn't fit inside aMangoModule.RequestIDPluginis a built-in reference implementation.mango/db.py—Database, one-line async engine + session factory + a FastAPIget_dbdependency (commit-on-success, rollback-on-error), replacing the ~15 lines every hand-rolled FastAPI project rewrites.mango/exceptions.py+mango/errors.py—MangoErrorand 5 subclasses (NotFoundError,ConflictError,ForbiddenError,UnauthorizedError,BadRequestError), each with a default HTTP status;register_error_handlers(app)wires them into FastAPI and installs a catch-all handler so an unhandled bug returns a generic 500 instead of leaking a traceback.mango/crud.py—build_crud_router(...)generates a full list/get/create/update/delete REST router from aMangoRepository+ Pydantic schemas — the biggest single boilerplate cut for a module that's plain CRUD.paginated=Truereturns amango.Pageenvelope instead of a bare list.mango/pagination.py—Page[T], a generic paginated-response schema (items/total/limit/offset);MangoRepository.list_page()/.search_page()return the(rows, total)it's built from.mango/auth.py—Auth: pluggable token-verification + user-loadingrequire_role/require/current_userFastAPI dependency factories — the "verify JWT -> load user -> check role -> 401/403" pattern every real app hand-writes, minus the token/storage specifics (those stay project-specific callables you provide).
mango/tasks.py+Database.spawn()— fire-and-forget background work with its own fresh DB session (never the request's, which closes before a background task finishes) and a logged, not silently swallowed, exception if it fails.mango/migrations.py—init_migrations(...)scaffolds a working, async-awarealembic.ini+migrations/env.pywired to a project's declarative Base, viamango init-migrations module.path:Base.mango/project.py—init_project(...)scaffolds a full project's folder structure (app/main.py,app/registry.py,app/db.py,app/modules/,tests/, project-root files) — seedocs/PROJECT_STRUCTURE.mdfor the convention and why each piece exists.mango/cli.py—mango init <project-name>scaffolds a project;mango new-module <name>scaffolds a startermodule.py;mango init-migrations <module.path:Base>scaffolds Alembic.examples/hello_module/— a complete, minimal module (one model, one repository, one endpoint) showing what mango collapses 5 files into.tests/— registry, topological ordering/cycle detection, repository behavior (core + extras), error-handler mapping, CRUD-router lifecycle (plain and paginated), auth guards, background tasks, migration scaffolding, project scaffolding, security middleware, and plugins — 43 tests, all runnable standalone..github/workflows/ci.yml— runs the full test suite on Python 3.11/3.12/3.13 on every push/PR, then builds the sdist+wheel.LICENSE(MIT),CHANGELOG.md,CONTRIBUTING.md.
Full usage guide, including every piece above with examples: docs/GUIDE.md.
Quickstart
cd mango
pip install -e ".[dev]"
pytest -q
Run the example app:
uvicorn examples.main:app --reload
curl http://127.0.0.1:8000/api/v1/hello
Starting a new project
mango init demo_shop
cd demo_shop
mango new-module items app/modules
# add `import app.modules.items.module` to app/registry.py
See docs/PROJECT_STRUCTURE.md for what
mango init scaffolds and why it's laid out that way.
Declaring a module
import mango
router = mango.Router()
@router.get("/hello")
async def hello():
return {"message": "hello from mango"}
@mango.module
class HelloModule(mango.MangoModule):
name = "hello" # required, unique
router = router # optional — modules like `admin` may have none
depends_on = () # names of other modules this one needs mounted first
# main.py — no fastapi import
import mango
app = mango.App(title="My API")
app.mount_all() # finds every @mango.module class, orders by depends_on, mounts it
app is directly ASGI-callable (uvicorn main:app) — equivalent to what
we hand-wrote in app/main.py for the 10-module collabfluenz.backend
reference project, but derived rather than maintained.
Status
Module registry + generic repository (with eager-loading, batch ops,
and exact-match filtering) + pagination + mount ordering + DB setup +
background tasks + auth guards + error-to-HTTP mapping + generated CRUD
routers + Alembic scaffolding + project scaffolding + production
hardening (security headers, opt-in rate limiting/CORS) + a plugin
extension point + a full FastAPI/Pydantic re-export surface (Router,
Schema, App, ...) are all in place — a project can be built end to
end importing only mango. CI runs the full suite across three Python
versions on every push. What's deliberately still project-specific: how
you verify a token and load a user (mango.Auth takes both as callables
— token providers and user storage vary too much to bake in a default),
and how you use Alembic once it's scaffolded (mango sets up the config,
not the migration authorship).
Honest gaps, unclosed: no real-world production usage beyond one
integration into collabfluenz.backend's mounting layer; 0.1.0
throughout this session's work (see CHANGELOG.md); a single
contributor. Nothing here fakes maturity mango doesn't have — see
CONTRIBUTING.md for how that changes.
The size goal
Every piece here exists to make a project's code shorter and more uniform, not to add abstraction for its own sake — that's the actual measure of whether an addition to mango is worth it. Concretely, per module:
| What you'd hand-write | Lines, roughly | With mango |
|---|---|---|
__init__.py re-exports |
15–30 | 0 — derived from the @mango.module class |
main.py router mounting |
1–2 per module | 0 — app.mount_all() |
| Generic repository CRUD | 30–50 | 0 — inherited from MangoRepository |
DB engine/session/get_db() |
~15 | 3 — mango.Database(url) |
| Plain-CRUD router (list/get/create/update/delete) | 60–90 | ~10 — mango.build_crud_router(...) |
| Domain-error -> HTTP status mapping | 5–10 per error site | 0 — raise mango.NotFoundError(...) |
| Auth guard (verify -> load -> role check) | 20–40 | ~10 — auth.require_role(...) |
| Background task + its own session | 10–15 | 1 — db.spawn(fn) |
Alembic env.py |
~100 | 0 — mango init-migrations |
| Security headers middleware | ~15 | 0 — on by default in App |
| Rate limiting middleware | ~30 | 1 — App(rate_limit=(100, 60)) |
New project skeleton (main.py, db.py, registry.py, ...) |
~80 | 0 — mango init <name> |
A module with real, non-generic business logic still writes that logic by hand — mango only removes the boilerplate around it, never the part that's actually specific to your app.
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 mangoframe-0.1.0.tar.gz.
File metadata
- Download URL: mangoframe-0.1.0.tar.gz
- Upload date:
- Size: 60.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87ae7a8a0813b2b6d7c3683d5546d33b035998f8368566e85d0fc12bb7ca5a41
|
|
| MD5 |
12e010b9d3bd8d081314efe27ec49f6d
|
|
| BLAKE2b-256 |
23568219f27a529065d250eb31ecbf0baad7014c69d450e27e19b0a55647da6c
|
File details
Details for the file mangoframe-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mangoframe-0.1.0-py3-none-any.whl
- Upload date:
- Size: 42.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11e9cc5dddd7003b89f622c7344bafea382b527b900975c9fc9e4a68b21ec050
|
|
| MD5 |
c8f4aaf2be9a9257b74f4c1cdb8537a5
|
|
| BLAKE2b-256 |
9fc4bc440f828b05153403f04007a3e3e6bee12a61fefc57cc165d934282f35a
|