Skip to content
Reference

Core Architecture

The HTTP, WebSocket, lifespan, background-task, and process-state architecture behind Flasgo.

Flasgo is built on ASGI and handles http, websocket, and lifespan scopes directly under servers such as Uvicorn.

At the center is the Flasgo application class. Incoming HTTP and WebSocket scopes are wrapped in framework objects, bound to request-local context variables, passed through security controls, and dispatched to the registered endpoint.

That model gives Flasgo:

  • Native async request handling
  • First-class WebSocket connections and ASGI startup/shutdown
  • Compatibility with modern ASGI deployment
  • A clean split between request parsing, routing, response coercion, and security enforcement
  • Request-local access to request, session, and current_user without sharing state across concurrent tasks
  1. The ASGI server forwards a request to the Flasgo app. Oversized request heads receive 431 before OpenTelemetry, routing, or session storage; these denials use a locally generated request ID even when incoming IDs are trusted.
  2. Flasgo constructs a Request and loads its session with the configured backend; invalid hosts skip shared-storage access. Body reads remain bounded and lazy.
  3. Dispatch validates the Host, with repeated security and authentication failures subject to the configured security-failure rate limit.
  4. Protected metrics and optional docs endpoints are handled before application routing and never inherit application CORS.
  5. The router identifies a route-specific CORS policy. A valid preflight can return 204 before CSRF checks, middleware, authentication, dependencies, or endpoint code.
  6. CSRF validation runs for ordinary requests, followed by before-request hooks that can short-circuit with a response.
  7. The router reuses the path/method match from CORS setup when available, or matches again if a before-request hook changed the target. Unmatched paths receive 404 and unsupported methods 405. Default IP rate limits run before authentication; authorization and identity-based limits follow. A missing or raising authentication backend produces 500, not 401.
  8. A route-specific endpoint plan binds typed request data, resolves dependencies, and invokes the handler. This includes standalone route/group dependencies and yielding providers.
  9. Flasgo normalizes the result (enforcing an explicit response_model when present) and runs after-request hooks. Function-scoped dependencies close before headers are sent.
  10. Flasgo applies eligible CORS and security headers and validates the response before persisting eligible session changes. It then adds framework cookies, validates again, and sends ASGI messages. Streaming responses await transport backpressure and watch for disconnects.
  11. Request-scoped dependencies close after sending completes or fails. Request context is then cleared. Background tasks run sequentially after successful complete delivery.

After-request hooks run for handler responses and for before-request short-circuits, 404 and 405 responses, rate-limit rejections, and authentication or authorization denials. Host, CSRF, and CORS preflight rejections, metrics and docs endpoint responses, and error-handler responses bypass them.

Failed dispatch restores the loaded session snapshot before invoking an error handler. Only a successfully returning custom error handler can persist deliberate changes from that error path. Returning an explicit error status normally does not roll back session changes. See Session Changes and Failures.

The framework creates a local request ID by default and includes it in the response and structured events. Metrics and OpenTelemetry spans use route templates to keep cardinality bounded; HTTP trace query values are redacted.

  1. Flasgo rejects oversized request heads before tracing, routing, or session loading, then matches the route.
  2. Flasgo validates the Host header and exact Origin, then runs default per-IP route limits before session loading or authentication.
  3. Flasgo loads the session, authenticates the request, and checks route permissions and identity-based rate limits before the handler can call accept().
  4. The handler uses bounded text, binary, or JSON message helpers.
  5. Message size, message rate, message type, JSON validity, subprotocol, and close-frame rules are enforced.
  6. Connection outcomes and duration are logged and optionally recorded as metrics.

WebSocket sessions are read-only: modifying the session during a connection does not write a cookie through the upgrade handshake.

An application may register one async-generator lifespan handler that yields exactly once. Code before the yield owns startup, while the finally path owns shutdown. Startup failures are reported to the server rather than leaving a partially initialized application, and the app can start again afterward. A handler that yields a second time has its cleanup run immediately and shutdown is reported as failed.

app.state is process-global and is intended for pools, clients, and immutable service objects. Request-specific data belongs in request context. Each worker process has its own state and metrics registry. Route quotas use an in-process limiter by default; the optional RedisRateLimiter coordinates decorated route quotas across workers. Security-failure and WebSocket message throttles remain process-local.

BackgroundTasks contains in-process best-effort work. Async functions are awaited, synchronous functions run in a thread, failures are logged, and later tasks continue. Durable work belongs on a durable queue.

Flasgo accepts several common return shapes and turns them into a response automatically.

Return value Result
str / bytes Text or raw body response
dict / list JSON response
None Empty 204 No Content response without Content-Type or Content-Length
(body, status) Response with explicit status, validated like any other response
(body, status, headers) Response with full tuple control
Response Used directly, then prepared with normal security/session processing
Dataclass instance Recursively serialized as JSON, omitting underscore-prefixed fields
Streaming response Sends a bounded async iterable with backpressure

With an explicit response_model=, output is projected and validated before response coercion; a raw Response cannot bypass that contract. See Response Contracts and Dependency Lifetimes for failure and cleanup boundaries.

The same app object can be run with the built-in development server for local work or under a production ASGI server for real deployments.

Related docs:

Sources: