apiquart
The minimalistic API framework for Quart — declarative request/response validation and automatic OpenAPI 3.1 docs, built on Pydantic v2.
apiquart brings APIFlask-style ergonomics to Quart's
async-native world. It is a thin layer over vanilla Quart and Pydantic: an
APIQuart app is a quart.Quart subclass, your schemas are plain Pydantic
models, and removing every decorator still leaves a working route.
@app.input— parse and validate the request (json/query/form/files/ headers/cookies/path); the validated model instance is injected as an argument.@app.output— serialize the return value (ORM object, dict, or model) through Pydantic.@app.doc— attach OpenAPI operation metadata.@app.auth_required— first-class auth via quart-httpauth.- OpenAPI 3.1 generated straight from your Pydantic models, with four
interactive doc UIs behind a single
/docsendpoint.
Installation
pip install apiquart
The distribution name is apiquart; the import name is apiquart. Optional
extras: apiquart[auth] (quart-httpauth), apiquart[yaml] (YAML spec output).
Quickstart
from apiquart import APIQuart, BaseModel, abort
app = APIQuart(__name__, title="Pets API", version="1.0.0")
class PetIn(BaseModel):
name: str
category: str = "other"
class PetOut(PetIn):
id: int
pets: dict[int, dict] = {}
@app.post("/pets")
@app.input(PetIn, arg_name="pet")
@app.output(PetOut, status_code=201)
async def create_pet(pet): # pet is a validated PetIn
pet_id = len(pets) + 1
pets[pet_id] = {"id": pet_id, **pet.model_dump()}
return pets[pet_id]
@app.get("/pets/<int:pet_id>")
@app.output(PetOut)
async def get_pet(pet_id):
if pet_id not in pets:
abort(404, message="Pet not found")
return pets[pet_id]
Run it with any ASGI server:
hypercorn module:app
- OpenAPI spec:
GET /openapi.json - Interactive docs:
GET /docs(Swagger UI by default; try?ui=redoc,?ui=rapidoc,?ui=elements)
Input locations
@app.input(Model, location=...) supports json (default), query, form,
files, form_and_files, headers, cookies and path. The validated model
is injected as f"{location}_data" unless you pass arg_name=. Multiple
@app.input decorators stack for different locations. Invalid input yields a
422 with the standard error envelope.
Output
@app.output(Model, status_code=200) validates and serializes the handler's
return value (model.model_validate(obj).model_dump(mode="json")). Handlers may
return obj, (obj, status), or (obj, status, headers). Use EmptyModel
with status_code=204 to document an empty response.
Errors
Every error is a single JSON envelope:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"detail": {"json": [{"loc": ["name"], "msg": "Field required", "type": "missing"}]}
}
}
Raise one with abort(404, message="Pet not found") or
raise HTTPError(...). Quart's own HTTPExceptions are reshaped into the same
envelope. Customize the shape with @app.error_processor.
Authentication
from quart_httpauth import HTTPTokenAuth
from apiquart import APIQuart
app = APIQuart(__name__)
auth = HTTPTokenAuth(scheme="Bearer")
@auth.verify_token
async def verify(token):
return await User.from_token(token)
@app.get("/admin/stats")
@app.auth_required(auth, roles="admin")
async def stats():
return {"user": auth.current_user()}
apiquart introspects the auth object and registers the matching OpenAPI security
scheme (Basic → http/basic, Bearer → http/bearer, custom header → apiKey).
Pagination
from apiquart import BaseModel, PaginationMeta, pagination_builder
class PetsPage(BaseModel):
data: list[PetOut]
pagination: PaginationMeta
@app.get("/pets")
@app.input(PetQuery, location="query")
@app.output(PetsPage)
async def list_pets(query_data):
items, total = await Pet.paginate(query_data.page, query_data.per_page)
return {
"data": items,
"pagination": pagination_builder(query_data.page, query_data.per_page, total),
}
Blueprints and class-based views
APIBlueprint carries the same decorators so blueprint routes contribute to the
spec. Quart's MethodView is supported — decorate the HTTP methods directly.
See examples/ for runnable apps.
License
MIT.
Release files for apiquart 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| apiquart-0.0.1.tar.gz | 161.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| apiquart-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 186.1 kB
Release files / apiquart-0.0.1.tar.gz
| Download URL | apiquart-0.0.1.tar.gz |
|---|---|
| Size | 161.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0e0eb1926855f43414e6e34f6c76d08e62c1474572a0f04283b128c845e6b140
|
|
BLAKE2b-256 checksum How to use checksums |
9ff0bb701c3d7ae0aa5b679cda6fb52f900560361902bc4f57677e4f1842cd47
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / apiquart-0.0.1-py3-none-any.whl
| Download URL | apiquart-0.0.1-py3-none-any.whl |
|---|---|
| Size | 24.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bdff2d39e7a0ed144a7a74c837a9f42af9c9ab2876df33f3c82df5633ab81703
|
|
BLAKE2b-256 checksum How to use checksums |
cd27b576b732829bc47f52047f915dbc23601508f506a400057e0c704d130e4f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|