Skip to main content

Safe typed query compilation for FastAPI.

Project description

Paramora

Paramora is safe typed query compilation for FastAPI. It turns HTTP query parameters into a backend-neutral AST, then emits backend-specific query objects. The current MVP is FastAPI-native and currently supports MongoDB output.

Status: 0.1 pre-release. The API is still allowed to change before the first public package release.

Documentation

The README gives the short path. The full user and contributor documentation is kept in the repository under docs/:

These docs live on the main branch so GitHub, source distributions, and PyPI readers can find the same authoritative material. The wheel only needs the runtime paramora package; docs do not need to be installed with the package.

Installation

Paramora is designed for FastAPI applications. Once the package is published on PyPI, install it with:

uv add paramora

For local development from the repository:

uv sync --group dev

Quickstart

Define a type-checker-friendly query contract with typing.Annotated, pass it to Query, and mount it with FastAPI using Depends(item_query).

from datetime import datetime
from typing import Annotated

from fastapi import Depends, FastAPI
from paramora import CompiledQuery, Query, QueryContract, query_field

app = FastAPI()


class ItemQuery(QueryContract):
    status: Annotated[str, query_field("eq", "in")]
    active: bool
    created_at: Annotated[
        datetime,
        query_field("gte", "lte", sortable=True),
    ]
    price: Annotated[float, query_field("eq", "gt", "gte", "lt", "lte")]


item_query = Query(ItemQuery, default_limit=20, max_limit=100)


@app.get("/items")
def list_items(query: CompiledQuery = Depends(item_query)):
    mongo = query.to_mongo()
    return list(
        collection
        .find(mongo.filter)
        .sort(mongo.sort)
        .skip(mongo.offset)
        .limit(mongo.limit)
    )

Request:

/items?status__in=free,busy&active=true&sort=-created_at&limit=20

Mongo output:

MongoQuery(
    filter={"status": {"$in": ["free", "busy"]}, "active": True},
    sort=[("created_at", -1)],
    limit=20,
    offset=0,
)

Modes

Paramora has one simple mode rule:

  • Query() has no contract and defaults to loose mode.
  • Query(MyContract) has a contract and defaults to strict mode.

Loose mode is useful for prototypes and internal tools:

loose_query = Query(default_limit=20, max_limit=100)

Strict mode is the recommended shape for public endpoints:

class ItemQuery(QueryContract):
    status: Annotated[str, query_field("eq", "in")]
    active: bool

item_query = Query(ItemQuery)

Strict mode rejects unknown fields, unknown operators, disallowed operators, invalid values, non-sortable sort fields, and invalid pagination values.

Contract fields

Bare annotations create equality-only filters:

class ItemQuery(QueryContract):
    active: bool

Use query_field(...) inside Annotated for operators, sortability, aliases, and required filters:

class ItemQuery(QueryContract):
    status: Annotated[str, query_field("eq", "in", "nin")]
    created_at: Annotated[
        datetime,
        query_field("gte", "lte", sortable=True, alias="createdAt"),
    ]

The positional operator API is deliberate: editors such as Pylance can provide better autocomplete for query_field("eq", "in") than for a nested tuple such as allow=("eq", "in").

Supported query syntax

Paramora supports Django-style query operators:

/items?status__in=free,busy&active=true&created_at__gte=2026-01-01&sort=-created_at&limit=20&offset=0

Supported operators in 0.1:

  • eq
  • ne
  • gt
  • gte
  • lt
  • lte
  • in
  • nin

A bare field defaults to equality, so ?active=true is equivalent to ?active__eq=true.

MongoDB backend

MongoDB is the first supported backend. Paramora currently emits:

  • Mongo filter dictionaries
  • PyMongo-compatible sort pairs
  • limit
  • offset
mongo = query.to_mongo()
collection.find(mongo.filter).sort(mongo.sort).skip(mongo.offset).limit(mongo.limit)

Other backends are planned after the public contract, AST, and error model are stable.

Error handling

Validation errors are structured and FastAPI-compatible:

{
  "detail": [
    {
      "loc": ["query", "price"],
      "msg": "Expected a float-compatible value.",
      "type": "query.type_error.float",
      "input": "bad"
    }
  ]
}

See Error handling for stable error code semantics.

Security notes

Paramora intentionally does not expose raw Mongo operators in query parameters. Use Paramora operators:

/items?price__gte=10

Do not expose raw backend syntax:

/items?price[$gte]=10
/items?price__$gte=10

Loose mode is schema-relaxed, not raw-database mode. Raw backend operators are still rejected by default.

Development

This repository is uv-first:

uv sync --group dev
uv run pytest -vv
uv run ruff format --check .
uv run ruff check .
uv run pyright

The default pytest configuration runs coverage with missing-line reporting. Mongo-like execution tests use mongomock; parser and coercion behavior are covered by focused unit tests.

See Development with uv and Testing strategy for the full workflow.

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

paramora-0.1.2.tar.gz (31.6 kB view details)

Uploaded Source

Built Distribution

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

paramora-0.1.2-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file paramora-0.1.2.tar.gz.

File metadata

  • Download URL: paramora-0.1.2.tar.gz
  • Upload date:
  • Size: 31.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.4

File hashes

Hashes for paramora-0.1.2.tar.gz
Algorithm Hash digest
SHA256 dca3e649b46ba1f2dab4261c2d48339b195546d3345e92cb9a485f11d0cf832d
MD5 6a56847fee83c2cb4cfee5a03b08ee68
BLAKE2b-256 3544a56016299b2cb9c905ce677baa48ba82049daca98870d0405b1e643e9ccb

See more details on using hashes here.

File details

Details for the file paramora-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: paramora-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 20.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.4

File hashes

Hashes for paramora-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ef81a0500a56fe9ede46ef7482f1f95cc3a663f49f6451acbaa36d518cdbd586
MD5 884b4788c79e61b84199b79802e40be2
BLAKE2b-256 63fedcbba2129597e25e105f0e2c4ae9c72971f037a14bf02df321e7b87d3dbb

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