Query syntax¶
Paramora uses a small backend-neutral HTTP query syntax. Clients do not send raw MongoDB operators or SQL fragments. They send field names, operator suffixes, sort directives, and pagination values.
Filter syntax¶
The default operator is equality:
This is equivalent to:
Other operators use Django-style double-underscore suffixes:
Supported operators¶
| Operator | Query parameter | Meaning |
|---|---|---|
eq |
field=value or field__eq=value |
equal to |
ne |
field__ne=value |
not equal to |
gt |
field__gt=value |
greater than |
gte |
field__gte=value |
greater than or equal to |
lt |
field__lt=value |
less than |
lte |
field__lte=value |
less than or equal to |
in |
field__in=a,b,c |
value is in list |
nin |
field__nin=a,b,c |
value is not in list |
Equality¶
With a strict contract, Paramora coerces values using the declared field types.
For example, active=true becomes True when active is declared as bool.
Range filters¶
MongoDB output merges range operators on the same field:
SQL output combines predicates with AND:
List filters¶
in and nin use comma-separated values:
Empty list filters are rejected:
In strict mode, every list item is coerced using the declared field type.
Sorting¶
Sorting uses the reserved sort parameter.
Ascending:
Descending:
In strict mode, the field must be declared with sortable=True:
class ItemQuery(QueryContract):
created_at: Annotated[datetime, query_field("gte", "lte", sortable=True)]
Sorting is opt-in because it can affect backend indexes and query performance.
Pagination¶
Paramora reserves limit and offset:
Query controls defaults and limits:
Rules:
- missing
limitusesdefault_limit - missing
offsetuses0 - both values must be integers
- both values must be greater than or equal to zero
limitcannot exceedmax_limit
Reserved parameters¶
These names are reserved by Paramora:
They are not parsed as filter fields.
Repeated parameters¶
When parsing a mapping-like input, Paramora currently uses the last value for a
repeated parameter. FastAPI integration receives repeated query parameters via
request.query_params.multi_items() and applies the same effective behavior.
For list filters, prefer comma-separated values:
rather than repeated keys.
Strict mode behavior¶
In strict mode, query parameters are validated against the declared contract.
Strict mode rejects:
- unknown fields
- unknown operators
- known operators that are not allowed for that field
- invalid values
- sorting by unknown or non-sortable fields
- missing required filters
- raw backend operator syntax
Example error:
{
"detail": [
{
"loc": ["query", "status__gte"],
"msg": "Operator 'gte' is not allowed for field 'status'.",
"type": "query.operator_not_allowed",
"input": "free"
}
]
}
Loose mode behavior¶
Loose mode is enabled with no contract:
Loose mode allows unknown fields and known Paramora operators. Unknown values are usually emitted as strings.
Loose mode still rejects raw backend syntax:
Loose mode is less schema-strict, not unsafe raw database passthrough.
Backend-neutral design¶
These query parameters are the same whether the backend is MongoDB or SQL:
Only the emitter changes: