Framework utility

Runtime

Between booting and shutting down, your app spends its life handling requests. WebFluid wraps each one in a context and gives you a handful of hooks to shape the request on its way in and the response on its way out. This is the layer that makes render, url_for and friends "just know" about the current request.

The request context

On every request the framework enters a FluidContext. From anywhere downstream — a route, a service, an event handler — you can grab it with FluidContext.current() to reach the active app and request without threading them through every function call. It's the same pattern our generated routes use:

fluid/app/index.py python
from webfluid.core.context import FluidContext


async def handle_request():
    ctx = FluidContext.current()
    user = ctx.request.session.get("user")
    return await ctx.fluid.render("index.html", user=user)
 

Outside of a request there is no context, and FluidContext.current() raises a RuntimeError. The framework's own helpers handle this gracefully by falling back to defaults, and so should yours when code might run from, say, a scheduled job.

Shaping requests and responses

Three decorators let you step into the request flow, all running inside that context:

  • before_request — runs before the route. Return nothing to continue, or return a response to short-circuit the request entirely.
  • after_request — receives the response and returns one (possibly a new one). Great for headers or post-processing.
  • context_processor — returns a dict that is merged into every render, so shared template variables don't have to be passed by hand.
fluid/runtime.py python
from webfluid.core.context import FluidContext


def register(app):

    @app.before_request
    def require_session():
        ctx = FluidContext.current()
        if ctx.request.url.path.startswith("/admin"):
            if not ctx.request.session.get("user"):
                from fastapi.responses import RedirectResponse
                return RedirectResponse("/login")

    @app.after_request
    async def add_header(response):
        response.headers["X-Powered-By"] = "WebFluid"
        return response

    @app.context_processor
    def globals_():
        return {"brand": "My Portal"}

This is exactly how the surface's processing layer works internally — it registers a context processor for the shared template variables, a before-request logger and an after-request hook that swaps in the styled error pages. Additives get their own scoped versions of all three, as we saw in the previous chapter.

Themes at runtime

With WF_THEMES enabled, theme selection is a request-time decision. You remember a choice in the session and read the active one back when rendering:

example python
from fastapi import Request


async def pick_theme(request: Request):
    ctx = FluidContext.current()
    ctx.fluid.set_theme(request, "ocean")   # stored in the session
    return await ctx.fluid.render("index.html")

Proxies and rate limiting

Two more runtime conveniences worth knowing. If PROXY_FIX is set, the fluid.asgi_app your server runs is wrapped in a proxy-headers middleware so client ips survive a reverse proxy. And the built-in slowapi limiter is reachable through fluid.limit for per-route limits on top of the global defaults:

example python
@app.get("/expensive")
@app.limit("5/minute")
async def expensive(request: Request):
    return {"ok": True}

Continue reading

From here you can continue straight with Logging.