Skip to main content

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.pyRouter, Depends, Query, Path, Body, Header, Cookie, Form, File, UploadFile, Request, Response, JSONResponse, status, HTTPException: the exact FastAPI classes, re-exported under mango. so route files never import fastapi.
  • mango/schema.pySchema, a pydantic.BaseModel subclass with from_attributes=True on by default (the setting every response schema in a DB-backed app needs but reliably forgets), plus Field, field_validator, model_validator re-exports.
  • mango/module.py@mango.module decorator + MangoModule base 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.pyMangoRepository[Model], a generic async repository with get/add/update/delete/list/count/search built in, plus get_or_404, exists, add_many, delete_many, filter_by(**equals), and options= (SQLAlchemy loader options, for eager-loading relationships) on get()/list().
  • mango/app.pyApp (owns its own FastAPI instance internally, directly ASGI-callable, the default entry point for a new project) and MangoApp (wraps an existing FastAPI app, for incremental adoption). Both mount every registered module's router in dependency order (topological sort on depends_on), raising a clear circular module dependency: a -> b -> a error instead of Python's ImportError ... most likely due to a circular import traceback. App also wires in production-hardening middleware by default (security_headers=True; opt-in rate_limit=/cors_origins=) and exposes use(plugin) + on_startup/on_shutdown hooks.
  • mango/security.pySecurityHeadersMiddleware (on by default) and RateLimitMiddleware (opt-in, in-memory sliding window — explicitly not a substitute for a real limiter in a horizontally-scaled deployment; see its docstring).
  • mango/plugins.pyPlugin protocol (install(app)) + App.use(plugin), the extension point for third-party/project-local code that doesn't fit inside a MangoModule. RequestIDPlugin is a built-in reference implementation.
  • mango/db.pyDatabase, one-line async engine + session factory + a FastAPI get_db dependency (commit-on-success, rollback-on-error), replacing the ~15 lines every hand-rolled FastAPI project rewrites.
  • mango/exceptions.py + mango/errors.pyMangoError and 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.pybuild_crud_router(...) generates a full list/get/create/update/delete REST router from a MangoRepository + Pydantic schemas — the biggest single boilerplate cut for a module that's plain CRUD. paginated=True returns a mango.Page envelope instead of a bare list.
  • mango/pagination.pyPage[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.pyAuth: pluggable token-verification + user-loading
    • require_role/require/current_user FastAPI 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.pyinit_migrations(...) scaffolds a working, async-aware alembic.ini + migrations/env.py wired to a project's declarative Base, via mango init-migrations module.path:Base.
  • mango/project.pyinit_project(...) scaffolds a full project's folder structure (app/main.py, app/registry.py, app/db.py, app/modules/, tests/, project-root files) — see docs/PROJECT_STRUCTURE.md for the convention and why each piece exists.
  • mango/cli.pymango init <project-name> scaffolds a project; mango new-module <name> scaffolds a starter module.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

mangoframe-0.1.1.tar.gz (62.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mangoframe-0.1.1-py3-none-any.whl (42.9 kB view details)

Uploaded Python 3

File details

Details for the file mangoframe-0.1.1.tar.gz.

File metadata

  • Download URL: mangoframe-0.1.1.tar.gz
  • Upload date:
  • Size: 62.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for mangoframe-0.1.1.tar.gz
Algorithm Hash digest
SHA256 d7fbb48674e31c384b9724499c534f8d6af4a8a5282c71cb577acc22f9f34a7b
MD5 12d20c2adfba1d75c01ffdb59def437c
BLAKE2b-256 129b48aca3fb2a7e0bfa6e7d595f9825864daed764824a4f30129fb324c64d87

See more details on using hashes here.

File details

Details for the file mangoframe-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: mangoframe-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 42.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for mangoframe-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4cb30d9ec73cb19eeb8178e9ac5985078257b328c5cde4d7acf576e319ae1a86
MD5 c8ead5e6aced2ec4c27779138d8e4ba7
BLAKE2b-256 547037f2a914f60f688355a7e8df3978d61e62dea0963968bd8f4b2ab8975f93

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page