Routes and handlers
Use @app.get, @app.post, @app.put, @app.patch, and @app.delete.
@app.post( "/accounts/{account_id}/items", body=ItemIn, response=ItemOut,)def create_for_account(item: ItemIn, account_id: int) -> ItemOut: return ItemOut(id=account_id, name=item.name, price=item.price)Handler binding
Section titled “Handler binding”The handler signature does not have to mirror URL order. Dexpot:
- binds path captures by name;
- treats the first non-path, non-
Requestparameter as the declared body; - preserves Python signature order;
- supports keyword-only parameters; and
- allows default-only parameters after the body parameter.
Registration fails before serving when a required parameter has no source, a path capture is not accepted, the handler uses *args or **kwargs, or another route owns the same method and structural path shape.
GET /users/{id} and GET /users/{name} conflict because only one can match a request.
Responses
Section titled “Responses”A handler may return:
- a JSON-encodable value;
- a msgspec struct; or
(status, payload).
response= precompiles the successful-response encoder. The current release does not yet enforce the returned type at runtime.
Annotation namespaces
Section titled “Annotation namespaces”Route decorators never read caller locals. Postponed annotations resolve against the handler module globals and captured closure bindings. For annotation-only aliases local to a factory or class, pass the exact bindings explicitly:
from __future__ import annotationsfrom dexpot import Dex
def build(): from dexpot import Request as Context
app = Dex()
@app.get("/context", annotation_locals={"Context": Context}) def context(request: Context): return {"method": request.method}
return appPass only the required names, not an entire frame’s locals(). Dexpot shallow-copies annotation_locals when the decorator is created.
Request context
Section titled “Request context”Annotate a parameter with the public Request type:
from dexpot import Request
@app.post("/accounts/{account_id}/items", body=ItemIn)def create_with_context(item: ItemIn, account_id: int, *, request: Request) -> dict: return { "account_id": request.params["account_id"], "method": request.method, "query": request.query, "same_body": request.body is item, }The request object is allocated only for handlers that ask for it. It is frozen and contains the decoded route values, raw query string, lowercase headers, received body bytes, and the same validated body object passed to the handler.