Skip to content

Request Reference

This page documents Request, State, and the request view objects available from handlers.

Prerequisites

Read Public API for binding rules. Use Request when you need headers, cookies, raw body access, auth, state, or call-source context.

python
from quater import FormData, Request, State, UploadFile

Request

Added in 0.1.0a1.

Normalized request object used by HTTP, MCP, and CLI paths.

python
Request(
    *,
    method: str,
    path: str,
    scheme: str = "http",
    headers: HeaderItems | Mapping[str, str] = (),
    query_string: str | bytes = "",
    body: RequestBody = None,
    auth: AuthContext | None = None,
    client: str | None = None,
    context: RequestContext | None = None,
    app: Quater | None = None,
    max_body_size: int | None = None,
    max_form_parts: int | None = None,
    max_form_field_size: int | None = None,
    max_file_size: int | None = None,
    upload_spool_size: int | None = None,
) -> None
ParameterTypeDefaultDescription
methodstrrequiredHTTP method. Quater stores it uppercase.
pathstrrequiredPath without query string.
schemestr"http"Request scheme. Quater stores it lowercase.
headersHeaderItems | Mapping[str, str]()Incoming request headers.
query_stringstr | bytes""Raw query string.
bodyRequestBodyNoneBytes, async body reader, or empty body.
authAuthContext | NoneNoneInitial auth context. Route auth usually sets it.
clientstr | NoneNoneClient address when available.
contextRequestContext | NoneNoneSource and entrypoint metadata.
appQuater | NoneNoneApp handling the request. Quater sets it at the app boundary.
max_body_sizeint | NoneNonePer-request body size limit.
max_form_partsint | NoneNonePer-request form field and file count limit.
max_form_field_sizeint | NoneNonePer-request string form field size limit.
max_file_sizeint | NoneNonePer-request uploaded file size limit.
upload_spool_sizeint | NoneNonePer-request upload spool threshold.

Normal app code receives a Request; it rarely constructs one directly outside tests.

Properties And Methods

MemberTypeDescription
methodstrUppercase method such as GET.
pathstrRequest path without query string.
schemestrhttp or https.
appQuater | NoneApp instance once the request enters Quater.
headersHeadersCase-insensitive header view.
queryQueryParamsParsed query parameters.
cookiesCookiesCookies parsed from the Cookie header.
authAuthContext | NoneAuth context returned by auth hooks.
stateStateRequest-local mutable state.
contextRequestContextSource, entrypoint, request id, tool, and action metadata.
clientstr | NoneClient address when available.
body()bytesReads and caches the request body.
json()AnyParses and caches the JSON body with Quater's JSON decoder.
form()FormDataParses and caches submitted form fields and files.

Example:

python
from quater import Quater, Request

app = Quater()


@app.get("/whoami")
async def whoami(request: Request) -> dict[str, object]:
    return {
        "source": request.context.source,
        "entrypoint": request.context.entrypoint,
        "request_id": request.context.request_id,
    }

Expected HTTP output:

json
{
  "source": "api",
  "entrypoint": "server",
  "request_id": "req_..."
}

State

Added in 0.1.0a1.

Attribute container for app-level and request-level state.

python
State() -> State

app.state lives as long as the app instance. request.state lives for one request.

python
@app.on_startup
async def startup() -> None:
    app.state.cache = {}


@app.get("/cache-size")
async def cache_size(request: Request) -> dict[str, int]:
    return {"size": len(request.app.state.cache)}

Do not store per-request values on app.state. Use request.state for those.

FormData

Added in 0.1.0a1.

Parsed form fields and uploaded files returned by Request.form().

python
FormData(
    *,
    fields: tuple[tuple[str, str], ...] = (),
    files: tuple[tuple[str, UploadFile], ...] = (),
) -> None

FormData behaves like a read-only mapping of string form fields. Normal mapping lookup returns the last value for a repeated field. Use get_all() when repeated field values matter.

Files live separately from fields because uploaded files are not strings. Use get_file() for one file and get_files() for repeated file fields.

MemberTypeDescription
fieldstuple[tuple[str, str], ...]All string field pairs in request order.
filestuple[tuple[str, UploadFile], ...]All uploaded file pairs in request order.
get_all(key)tuple[str, ...]All string values for a field.
get_file(key)UploadFile | NoneLast uploaded file for a field.
get_files(key)tuple[UploadFile, ...]All uploaded files for a field.
python
from quater import Quater, Request

app = Quater()


@app.post("/profile")
async def profile(request: Request) -> dict[str, object]:
    form = await request.form()
    avatar = form.get_file("avatar")
    return {
        "name": form["name"],
        "avatar": avatar.filename if avatar else None,
    }

UploadFile

Added in 0.1.0a1.

Uploaded multipart file passed to handlers using File markers.

python
UploadFile(
    *,
    filename: str,
    content_type: str,
    headers: Mapping[str, str] | None = None,
    content: bytes = b"",
    spool_size: int = 1048576,
) -> None

Quater strips path components from submitted filenames before it creates UploadFile. Treat filename as display metadata, not a safe storage path. spool_size controls when the underlying temporary file rolls from memory to disk.

MemberTypeDescription
filenamestrSanitized client filename without directory components.
content_typestrFile part content type, or application/octet-stream.
headersdict[str, str]File part headers with lowercase names.
sizeintUploaded byte count.
fileBinaryIOUnderlying spooled binary file object.
closedboolWhether Quater has closed the underlying file.
read(size=-1)bytesRead bytes from the current file position.
seek(offset, whence=0)intMove the file cursor and return the new position.
close()NoneClose the underlying file. Quater also closes it after the response.
python
from quater import File, Quater, UploadFile

app = Quater()


@app.post("/imports")
async def import_document(document: UploadFile = File()) -> dict[str, object]:
    content = await document.read()
    return {
        "filename": document.filename,
        "content_type": document.content_type,
        "size": len(content),
    }

Headers, QueryParams, and Cookies are request views. They are not top-level public imports, but you will read them from Request.

Headers

Case-insensitive mapping.

python
token = request.headers.get("authorization")
all_cookies = request.headers.get_all("set-cookie")
raw_pairs = request.headers.raw

QueryParams

Parsed query-string mapping. Normal lookup returns the last value. Use get_all() for repeated keys.

python
# /search?tag=paid&tag=vip
request.query.get("tag")
request.query.get_all("tag")

Cookies

Parsed cookie mapping.

python
session_id = request.cookies.get("session")

RequestContext

request.context tells you how the handler was reached.

FieldTypeDescription
source"api" | "mcp" | "cli"Surface that reached the handler.
entrypoint"server" | "local"Hosted request or local CLI call.
request_idstr | NoneCorrelation id.
tool_namestr | NoneMCP tool name for tool calls.
action_namestr | NoneCLI action name for action calls. MCP tool calls also set it to the tool name.

What Can Go Wrong

Payload Too Large : await request.body() exceeded max_body_size.

Malformed JSON body : await request.json() could not decode valid JSON.

Malformed form body : await request.form() could not decode submitted form data.

Unsupported form content type : The route expected form data, but the request did not use application/x-www-form-urlencoded or multipart/form-data.

request.auth is None : The route had no auth hook, or auth failed before the handler. Check before reading request.auth.subject.

Also See

  • Public API: usage patterns.
  • Security: request id validation and access logs.
  • Testing: constructing requests through TestClient.

Released under the MIT License.