On this page
Core API reference for fastware: ASGI type aliases, 5 response types, request parsing, path-based routing, WebSocket helpers, and the app factory.
#Core API Reference
This page documents fastware's core modules: the foundational types, response classes, request handling, routing, WebSocket support, and the application factory. These are the building blocks for every fastware application.
#ASGI Types
Low-level ASGI type aliases (Scope, Receive, Send, ASGIApp, Middleware) used throughout fastware. These 5 type aliases are re-exported from the top-level package for convenience and provide consistent type-checking across the entire middleware and routing stack.
#src.fastware.types
ASGI type aliases (Scope, Receive, Send, ASGIApp, Middleware) used throughout fastware for consistent type-checked request and response handling.
#Response Types
HTTP response types for returning data from route handlers. Handlers can return any of these types, or plain dict/list values (which are automatically wrapped in JSONResponse). Also includes cookie helpers and the HTTPError exception for error responses.
#src.fastware.responses
HTTP response types including JSON, text, HTML, bytes, and streaming responses, plus cookie helpers and low-level ASGI send functions.
#set_cookie
def set_cookie(name: str, value: str, *, httponly: bool=False, samesite: str='lax', max_age: int | None=None, path: str='/', secure: bool=False) -> strBuild a Set-Cookie header string.
#delete_cookie
def delete_cookie(name: str, *, path: str='/') -> strBuild a Set-Cookie header string that clears the cookie.
#HTTPError
Raise from handlers to return a specific HTTP error status.
#JSONResponse
JSON response with optional status code, headers, and cookies.
#TextResponse
Plain text or CSS response.
headers (optional) is merged with the framework's default response headers; values must already be plain strings.
#HTMLResponse
HTML response.
#BytesResponse
Raw bytes response with an explicit content type.
#StreamResponse
Streaming response (for SSE).
#FileResponse
Serve a file from disk with MIME detection and Content-Length.
#_send_response
async def _send_response(send: Callable, status: int, body: bytes, content_type: str, extra_headers: dict[str, str] | None=None, cookies: list[str] | None=None) -> NoneSend a complete HTTP response (headers + body).
#send_error
async def send_error(send: Callable, status: int, detail: str) -> NoneSend a complete JSON error response via raw ASGI send calls.
Intended for use by middleware that needs to short-circuit with an error without constructing response objects.
#Request Handling
The Request wrapper provides lazy body parsing, query parameter extraction with type coercion and validation, header access, cookie parsing, and per-request state. Handlers receive a Request as their first argument.
#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.
#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 helper decodes both 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) -> boolCheck whether the client has disconnected.
#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 request body as a Pydantic model. Raises HTTPError(422) on failure.
#Routing
Path-based HTTP router with {param} placeholder syntax, typed parameters ({id:int}), greedy path segments ({path:path}), and sub-router composition via include_router. Supports all 7 standard HTTP methods (GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD) and WebSocket routes.
#src.fastware.routing
Path-based HTTP router with curly-brace parameter placeholders, automatic type coercion, method-based dispatch, and route group composition.
#_parse_segment
def _parse_segment(seg: str) -> tuple[str | None, str | None, type | None]Parse a route pattern segment into (literal, param_name, converter).
Returns one of:
- (literal_str, None, None) for plain segments like "api"
- (None, param_name, converter) for parameterized segments like {id:int}
- (None, param_name, None) for :path segments (greedy)
#Router
Simple path-based HTTP router using {param} placeholders and type coercion.
Supports {param}, {param:str}, {param:int}, and {param:path} syntax.
#mount
def mount(self, prefix: str, app: Any) -> NoneMount an ASGI sub-application at a path prefix.
When a request path starts with prefix, the scope is rewritten (path stripped, root_path extended) and forwarded to app. Both http and websocket scope types are forwarded.
The prefix must start with / and must not end with /. A trailing slash is stripped automatically.
#get
def get(self, path: str, *, deps: dict[str, Callable] | None=None, response_model: type | None=None) -> CallableDecorator to register a GET handler.
#post
def post(self, path: str, *, deps: dict[str, Callable] | None=None, response_model: type | None=None) -> CallableDecorator to register a POST handler.
#delete
def delete(self, path: str, *, deps: dict[str, Callable] | None=None, response_model: type | None=None) -> CallableDecorator to register a DELETE handler.
#put
def put(self, path: str, *, deps: dict[str, Callable] | None=None, response_model: type | None=None) -> CallableDecorator to register a PUT handler.
#patch
def patch(self, path: str, *, deps: dict[str, Callable] | None=None, response_model: type | None=None) -> CallableDecorator to register a PATCH handler.
#add_route
def add_route(self, method: str, path: str, handler: Callable, *, deps: dict[str, Callable] | None=None, response_model: type | None=None) -> NoneProgrammatic route registration.
#ws
def ws(self, path: str, *, deps: dict[str, Callable] | None=None) -> CallableDecorator to register a WebSocket handler.
#add_ws_route
def add_ws_route(self, path: str, handler: Callable, *, deps: dict[str, Callable] | None=None) -> NoneRegister a WebSocket handler for a path pattern (supports {param}).
#include_router
def include_router(self, other: Router, prefix: str | None=None, deps: dict[str, Callable] | None=None) -> NoneCopy all routes from other into this router.
If prefix is given (e.g. "/api/v1"), its segments are prepended to every copied route's pattern.
If deps is given (a dict mapping names to factory callables), they are merged into each copied route's deps. Router-level deps are listed first so that per-handler deps can override them.
#match
def match(self, method: str, path: str) -> tuple[Callable, dict[str, Any]] | NoneReturn (handler, path_params) or None if no route matches.
Path parameter values are coerced to their declared types (e.g., {id:int} produces an int). If coercion fails the route does not match, allowing fall-through to 404.
#_match_with_deps
def _match_with_deps(self, method: str, path: str) -> tuple[Callable, dict[str, Any], dict[str, Callable], type | None] | NoneReturn (handler, path_params, deps, response_model) or None.
Internal variant of :meth:match that also returns the merged dependency dict and response_model for the matched route. Used by create_app for DI resolution and response validation.
#_match_with_path_param
def _match_with_path_param(pattern: list[ParsedSegment], segments: list[str], path_idx: int) -> dict[str, Any] | NoneMatch a route pattern containing a :path greedy parameter.
Literal/typed segments before the :path param must match exactly. Literal/typed segments after the :path param are matched from the end of the path. Everything in between is consumed by the :path parameter (joined with "/").
#match_ws
def match_ws(self, path: str) -> tuple[Callable, dict[str, Any]] | NoneReturn (handler, path_params) for a WebSocket path, or None.
#_match_ws_with_deps
def _match_ws_with_deps(self, path: str) -> tuple[Callable, dict[str, Any], dict[str, Callable]] | NoneReturn (handler, path_params, deps) for a WebSocket path, or None.
Internal variant of :meth:match_ws that also returns deps.
#WebSocket
WebSocket helper class wrapping the raw ASGI scope/receive/send triple into a convenient interface with accept, close, send_json, receive_json, send_text, receive_text, and similar methods. Handlers registered via router.ws() receive a WebSocket instance instead of a raw Request.
#src.fastware.websocket
WebSocket helper class wrapping the raw ASGI scope/receive/send triple with typed accept, send, receive, and close methods for ergonomic usage.
#WebSocketDisconnect
Raised when a WebSocket client disconnects.
The code attribute carries the close code from the ASGI websocket.disconnect message (defaults to 1000 / normal closure).
#WebSocket
Wraps the raw ASGI (scope, receive, send) triple for WebSocket connections.
Handlers receive a WebSocket instance instead of the raw triple, providing convenient methods for accept/close/send/receive and properties for path_params, headers, and query_string.
#path_params
def path_params(self) -> dict[str, Any]#headers
def headers(self) -> dict[str, str]Parse ASGI headers into a case-preserving dict (first value wins).
#query_string
def query_string(self) -> str#accept
async def accept(self, subprotocol: str | None=None) -> None#close
async def close(self, code: int=1000) -> None#send_json
async def send_json(self, data: Any) -> None#send_bytes
async def send_bytes(self, data: bytes) -> None#send_text
async def send_text(self, text: str) -> None#_receive_data
async def _receive_data(self) -> dict[str, Any]Receive a data message, raising WebSocketDisconnect on disconnect.
#receive_json
async def receive_json(self) -> Any#receive_bytes
async def receive_bytes(self) -> bytes#receive_text
async def receive_text(self) -> str#receive_raw
async def receive_raw(self) -> dict[str, Any]Return the raw ASGI message dict from the WebSocket connection.
The dict contains keys like "type", "bytes", "text" depending on the frame type. Useful for handlers that need to distinguish between binary and text frames without committing to one receive method.
#Application Factory
The create_app function assembles a Router, optional middleware, static file serving, SPA fallback, lifespan management, and built-in middleware (CORS, request ID, request timing, trusted hosts, Vite dev proxy) into a single ASGI application callable.
| Field | Type | Default | Description | |
|---|---|---|---|---|
middleware | list[Callable] | None | None | |
static_dir | Path | None | None | |
static_path | str | '/assets' | ||
spa_fallback | Path | None | None | |
api_prefix | str | None | None | |
lifespan | Callable | None | None | |
name | str | None | None | |
exception_handlers | dict[type, Callable] | None | None | |
dependency_overrides | dict[Callable, Callable] | None | None | |
cors_origins | list[str] | None | None | |
trusted_hosts | list[str] | None | None | |
request_id | bool | True | ||
request_timing | bool | True | ||
vite_dev_port | int | None | None |
#src.fastware.app
ASGI application factory with middleware chain composition, static file serving, SPA fallback routing, async lifespan hooks, and WebSocket support.
#_send_stream
async def _send_stream(send: Callable, resp: StreamResponse) -> NoneSend a streaming HTTP response, iterating the async generator.
#_deep_convert_pydantic
def _deep_convert_pydantic(obj: Any) -> AnyRecursively convert Pydantic models to plain dicts/lists.
Walks dicts and lists, calling .model_dump(mode="json") on any object that has model_dump (i.e. Pydantic BaseModel instances).
#_send_result
async def _send_result(send: Callable, result: Any) -> NoneDispatch a handler return value to the appropriate sender.
#_serve_static
async def _serve_static(send: Callable, static_dir: Path, rel_path: str) -> boolServe a static file. Returns True if served, False if not found.
#_serve_spa_fallback
async def _serve_spa_fallback(send: Callable, spa_fallback: Path) -> NoneServe the SPA fallback file (typically index.html).
#AppConfig
Configuration for :func:create_app.
All fields correspond to the keyword arguments of create_app. Pass an AppConfig instance as the config parameter, and/or supply individual keyword arguments. Keyword arguments override matching fields on the config object.
#create_app
def create_app(router: Router, config: AppConfig | None=None, **kwargs: Any) -> CallableCreate an ASGI application callable.
Accepts an optional config (:class:AppConfig) and/or keyword arguments. Keyword arguments override matching fields on the config object. If neither is supplied, defaults from AppConfig are used.
If api_prefix is set (e.g. "/api"), the SPA fallback will not serve index.html for paths that start with the prefix -- they fall through to the 404 handler instead.
Built-in middleware (applied when their parameters are truthy):
trusted_hosts: TrustedHostMiddleware (outermost)vite_dev_port: ViteDevProxycors_origins: CORSMiddlewarerequest_id: RequestIDMiddlewarerequest_timing: RequestTimingMiddleware (innermost)
Custom middleware supplied via middleware wraps after built-in middleware (between the app and the built-in stack).