Architecture and design
=======================

MicroPie is intentionally small.  Its goal is to provide just enough
functionality to build asynchronous web applications without locking
you into a rigid structure.  This section discusses some of the key
design choices and how they influence the API.

ASGI application
----------------

MicroPie is built on top of the `ASGI`_ specification, which defines
how Python applications communicate with asynchronous servers.  Your
application subclass inherits from :class:`~micropie.App` and
implements the ASGI call interface via the ``__call__`` method.  The
framework inspects the ``scope['type']`` field to dispatch to HTTP,
WebSocket or lifespan handling code.

Routing by naming
-----------------

Unlike frameworks that require you to declare routes in advance,
MicroPie derives routing information from the names of your methods.
The first segment of the path becomes the handler name.  This allows
you to add new endpoints simply by defining a new method on your
application subclass.  The remaining path segments, query string
parameters and form data are mapped to arguments via function
signature inspection.  This approach reduces boilerplate but may
surprise users expecting explicit routing.  You can always regain
control by writing a custom middleware to map paths to handlers or by
checking ``self.request.method`` and ``self.request.path_params`` inside
your handlers.

Context‑local request
---------------------

MicroPie stores the current request in a :mod:`contextvars`
ContextVar.  This allows you to access ``self.request`` within your
handler as well as from deeper helper functions without passing the
request around explicitly.  The context variable is reset at the end
of each request to avoid leaking state.

Session management
------------------

Sessions are stored in a pluggable back‑end and identified by a
random ``session_id`` cookie.  The default in‑memory back‑end keeps
session data in dictionaries keyed by session ID and updates a last
access timestamp to implement expiration.  You can customise the
back‑end by providing your own implementation of
:class:`~micropie.SessionBackend`.  MicroPie saves sessions after a
handler returns, only if ``self.request.session`` is non‑empty, to
avoid creating unnecessary cookies.

Middleware pipeline
-------------------

The middleware hooks allow you to intercept requests before and after
handlers.  Middleware can modify the request object, provide early
responses (useful for authentication), and alter the final response.
For WebSockets, separate middleware hooks run before the connection
handler starts and after it finishes.

Streaming and SSE support
-------------------------

Handlers may return an iterator or asynchronous generator to stream
data to the client.  MicroPie detects such responses and iterates
over them, sending each chunk to the client.  When using server‑sent
events (SSE) there is an additional challenge: if the client
disconnects, the iterator must be cancelled.  MicroPie wraps SSE
responses in a small loop that listens for disconnect events and
cancels the generator accordingly.  Remember to include a
``Content‑Type: text/event-stream`` header when sending SSE.

Lifespan hooks
--------------

ASGI defines a lifespan protocol for startup and shutdown events.  MicroPie
exposes ``startup_handlers`` and ``shutdown_handlers`` lists on the
:class:`~micropie.App` instance.  Handlers are executed sequentially and
should be asynchronous callables.  Use them to open database connections,
prime caches or register background tasks.  Lifespan functions run before
any request or WebSocket traffic is accepted, ensuring your dependencies
are ready.

Templating and JSON helpers
---------------------------

If :mod:`jinja2` is installed, MicroPie enables the
:meth:`~micropie.App._render_template` helper to render templates from a
``templates`` directory, returning HTML responses with the correct
``Content-Type``.  For JSON, the framework prefers :mod:`orjson` when
available and gracefully falls back to :mod:`json`.  This keeps the core
lean while letting you opt into performance boosts.

Error handling
--------------

MicroPie automatically handles common error cases.  Requests for
unknown routes result in a ``404 Not Found``.  If a required
parameter is missing, MicroPie responds with ``400 Bad Request``.
Unhandled exceptions inside handlers produce a ``500 Internal Server
Error`` and are printed to standard error.  You can override these
behaviours via middleware.

Extensibility
-------------

The minimalist core is designed to be extended.  You can mount your
application behind other ASGI middleware, integrate additional
protocols like Socket.IO, or implement your own session storage.  The
framework imposes few constraints so that you remain in control of
your stack.

WebSocket pipeline
------------------

WebSocket connections follow a parallel flow to HTTP requests.  The
``ws_`` method naming convention resolves handlers, middleware gates the
connection before :meth:`~micropie.WebSocket.accept` is called, and the
:class:`~micropie.WebSocket` helper manages receive/send coroutines.  Session
data is shared with HTTP handlers so users can authenticate once and reuse
the same session across protocols.

.. _ASGI: https://asgi.readthedocs.io/