beanie
Async Python ODM for MongoDB built on Pydantic, with pythonic queries, document links, caching, and schema migrations.
Repository Health
Technical Analysis
Beanie is an asynchronous Python object-document mapper (ODM) for MongoDB. Document models are plain Pydantic models, so validation, serialization, and typing come for free, while Beanie layers on the parts a raw PyMongo/Motor client leaves you to build yourself: an expression-based query builder (Product.find_one(Product.price < 10)), relation following via Link/BackLink fields, an LRU document cache, before/after event hooks, and a migration runner for evolving schemas over time.
It sits on top of PyMongo’s native async client, so there’s no separate driver to manage, and it ships a beanie migrate CLI for running iterative or free-fall data migrations. A synchronous sibling project (Bunnet) exists for non-async codebases, but Beanie itself targets asyncio applications such as FastAPI services that need a MongoDB layer without hand-rolling collection access, validation, and migrations separately.
What You Get
- Document models that are ordinary Pydantic models — validation, serialization, and JSON schema generation come from Pydantic directly
- A pythonic query builder that turns comparisons like
Product.price < 10into MongoDB filter documents LinkandBackLinkfields for relation-following between documents, with configurable fetch/nesting depth and cascading write/delete rules- A built-in per-document LRU cache to avoid redundant round-trips for repeated reads
before_event/after_eventhooks keyed to Insert, Replace, Save, Update, and Delete for cross-cutting document logic- A
beanieCLI and migration runner supporting both iterative and free-fall migration strategies for evolving schemas over time
Common Use Cases
- Backing a FastAPI (or other asyncio) service’s data layer with MongoDB without writing raw PyMongo query/update dictionaries by hand
- Modeling documents with nested Pydantic sub-models and typed, indexed fields instead of loosely-typed dicts
- Following relations between collections (e.g. orders referencing products) via
Linkfields instead of manual$lookupaggregation or extra queries - Running versioned schema/data migrations against a live MongoDB deployment as models evolve
Under The Hood
Architecture
The Document class (beanie/odm/documents.py) composes behavior through a stack of dedicated interface mixins — SettersInterface, InheritanceInterface, FindInterface, AggregateInterface, and OtherGettersInterface — layered on top of LazyModel, so finding, aggregating, and mutating documents are each owned by a separate, swappable module rather than crammed into one class. Query construction lives in beanie/odm/operators and beanie/odm/queries, which translate expression comparisons on class-level fields into MongoDB filter and update documents; relations are resolved through Link/BackLink descriptors in beanie/odm/fields.py, which the settings and parsing layers (beanie/odm/settings, beanie/odm/utils/parsing.py) walk to fetch and merge nested documents. Schema evolution is handled by an entirely separate beanie/migrations package (runner.py, controllers/iterative.py, controllers/free_fall.py), invoked through a distinct CLI entry point (beanie/executors/migrate.py) rather than being bolted onto the document layer. This gives the project a genuinely modular, layered structure, though the interface-mixin composition means changes to the core Document abstraction ripple through every mixin and the query/operator layer that assumes its Pydantic-model shape.
Tech Stack
Beanie targets Python 3.10-3.13 and is built directly on Pydantic 2.4+ for modeling/validation, lazy-model for deferred attribute parsing, and PyMongo 4.11+‘s native async client as the MongoDB driver — there is no separate Motor dependency. click powers the beanie migrate CLI, and typing-extensions backfills newer typing features across supported Python versions. Optional extras expose PyMongo’s own aws, gssapi, encryption, ocsp, snappy, and zstd variants, plus a queue extra that pulls in the separate beanie-batteries-queue package. The project builds via flit_core (PEP 517), documents itself with mkdocs-material and pydoc-markdown, and publishes to PyPI through a dedicated GitHub Actions workflow.
Code Quality
The repository carries 110 test files against 81 source files, exercising documents, queries, operators, custom types, both migration strategies, FastAPI integration, and static typing (tests/typing) — run via pytest-asyncio with pytest-cov enforcing an 80% coverage floor declared in pyproject.toml. Errors are modeled as explicit typed exceptions in beanie/exceptions.py (CollectionWasNotInitialized, DocumentNotFound, RevisionIdWasChanged, ReplaceError, and others) rather than swallowed generically. ruff handles both linting and formatting and mypy/pyright handle type checking, both wired into .pre-commit-config.yaml and pre-commit.ci, with a separate GitHub Actions workflow running the test suite on every change. The package self-declares as fully typed via its Typing :: Typed classifier.
What Makes It Unique
What separates Beanie from a raw PyMongo/Motor client plus hand-rolled schema code is the combination it bundles for one database: Pydantic-native models with pythonic comparison-based queries, an ORM-style relation system (Link/BackLink with configurable nesting depth and cascading WriteRules/DeleteRules), a per-class LRU cache, lifecycle event hooks (before_event/after_event keyed to Insert/Replace/Save/Update/Delete), and a first-class migration runner supporting both iterative and free-fall strategies — comparable in spirit to Alembic, but built specifically for MongoDB’s document model. Individually these ideas exist elsewhere in the ODM/ORM space, but bundling relation-following, caching, and versioned migrations together on top of Pydantic is a broader feature set than most single-purpose MongoDB ODMs offer.