On this page
Request wrapper with lazy msgspec JSON parsing, json_as() decoding, typed query-parameter extraction, header/cookie access, and State object.
#src.fastware.request
#src.fastware.request
HTTP request wrapper providing lazy body parsing, query parameter extraction, JSON deserialization via msgspec, header access, and per-request state.
#State
Dict-backed state that supports both attribute and dict access.
state.key, state["key"], and state.get("key") all work. Attribute assignment (state.key = val) also works.
#get
def get(self, key: str, default: Any=None) -> Any#Request
Wraps ASGI scope with parsed body and params.
#json
def json(self) -> dict | list | NoneLazily decode the JSON body on first access, then cache.
#body
def body(self) -> bytes | NoneRaw request body bytes.
#_qs
def _qs(self) -> dict[str, list[str]]Full parse_qs of the query string (name -> list of values).
Parsed once on first access and cached, then shared by query(), query_list(), and query_params so the query string is never re-parsed per call.
#query
def query(self, name: str, default: Any=_MISSING, *, type_: type=str, ge: int | float | None=None, le: int | float | None=None, min_length: int | None=None, max_length: int | None=None) -> AnyGet a query parameter by name, with optional type conversion and constraints.
The default is only used when the key is absent from the query string. When no default is given and the key is absent, returns None.
Type coercion failure (key present but unconvertible) always raises HTTPError(422) -- the default is not used as a fallback for bad input.
Constraints (checked after type coercion):
ge: value must be >= this (numeric)le: value must be <= this (numeric)min_length:len(value)must be >= this (strings)max_length:len(value)must be <= this (strings)
Raises HTTPError(422) on coercion failure or constraint violation.
#query_list
def query_list(self, name: str, type_: type=str) -> listReturn all values for a multi-value query key with optional type coercion.
Returns an empty list if the key is absent. Raises HTTPError(422) if any value cannot be converted to type_.
#query_params
def query_params(self) -> dict[str, str]Parsed query string as a dict (first value per key). Cached.
#header
def header(self, name: str, default: str | None=None) -> str | NoneReturn a request header by name (case-insensitive).
ASGI headers arrive as a list of (bytes, bytes) tuples. This decodes to str and looks the name up case-insensitively, matching HTTP semantics.
#body_size
def body_size(self) -> intLength in bytes of the raw request body (0 if no body).
#state
def state(self) -> StateLifespan + per-request state, supporting both attribute and dict access. Cached.
#method
def method(self) -> strHTTP method (GET, POST, etc.).
#path
def path(self) -> strRequest path.
#is_disconnected
async def is_disconnected(self) -> boolReturn whether the client has disconnected.
This is a pure read of a flag maintained by the app's disconnect watcher, which is the single owner of the ASGI receive channel for the request's post-body lifetime. Because it never touches receive (no channel peeking, no receive task), it is safe to call any number of times from both streaming and non-streaming handlers and never competes with the watcher for the single receive channel.
#_mark_disconnected
def _mark_disconnected(self) -> NoneRecord a client disconnect. Called by the app's disconnect watcher.
Sets the flag and cancels any in-flight stream-driving task so a generator blocked between yields is unwound promptly (running its finally cleanup) instead of leaking until process exit.
#_register_stream_task
def _register_stream_task(self, task: Any) -> NoneRegister the task driving a streaming response body so the watcher can cancel it on disconnect.
#cookies
def cookies(self) -> dict[str, str]Parse Cookie header and return a dict of cookie name-value pairs.
#cookie
def cookie(self, name: str, default: str | None=None) -> str | NoneGet a single cookie value by name.
#json_as
def json_as(self, model: type)Parse the request body into model, dispatching on the target type.
If model is a msgspec.Struct subclass, the raw body is decoded with msgspec.json.decode(body, type=model) -- the fast, msgspec-native path the framework is built around. Otherwise the body falls back to the Pydantic path (model.model_validate). Either way, a decode/validation failure raises HTTPError(422).