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 UUIDv4 ``session_id`` cookie by default.  Incoming IDs are validated
before they reach the back-end; custom back-ends can supply a matching ID
factory and validator.  When an empty session becomes populated,
MicroPie rotates the ID so a client-provided cookie cannot be fixed across
that state transition.  Applications can request another rotation with
:meth:`~micropie.Request.regenerate_session` when an existing session gains
privileges.  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`.

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.  HTTP request bodies are parsed
before ``before_request`` runs, so byte-counting upload limits belong in
the :class:`~micropie.App` body-limit settings or in an outer ASGI/proxy
layer rather than in :class:`~micropie.HttpMiddleware`.

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 reported through the standard ``micropie`` logger.  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/
