Framework utility

Lifecycle

We've been name-dropping "lifecycle hooks" since the very first extension. Time to make good on it. Every WebFluid app has a managed lifecycle: it boots, serves, and shuts down again, all kicked off by the single fluid.mix() call you've used since Getting Started.

What mix() really does

mix() is just a thin wrapper that runs the async start() coroutine. From there the framework opens a logging session, runs your startup hooks, spins up the server, and then sits on a shutdown flag. When a stop signal arrives it serves its last request, runs your shutdown hooks and exits cleanly. You never manage that loop yourself — you only register the things that should happen at each end.

Startup and shutdown hooks

A hook is a plain (sync or async) callable that takes no required arguments. You register it with startup_hook or shutdown_hook. This is the proper home for the table creation we hard-coded under if __name__ == "__main__" back in the SQLAlchemy chapter:

main.py python
from webfluid import Fluid
from webfluid.core.ext import db


def create_app() -> Fluid:
    app = Fluid(__name__)

    from fluid.api import api_router
    app.include_router(api_router)

    @app.startup_hook
    async def create_tables():
        bind = db.get_bind_for_model(db.Model)
        db.Model.metadata.create_all(bind.sync_engine)

    @app.shutdown_hook
    def goodbye():
        from webfluid.utils.logging import factory as log
        log.log("Portal is cooling down.")

    return app


if __name__ == "__main__":
    create_app().mix()

Extensions and Additives use exactly this mechanism under the hood — the scheduler starts on a startup hook, the proxy client closes on a shutdown hook, and even the Additive registration itself is a startup hook. So when you register your own, you're joining the same queue the framework uses.

 

Startup hooks are gathered and run concurrently, not one after another. Don't rely on one hook finishing before another begins; if you need ordering, do the ordered work inside a single hook. The same goes for shutdown hooks (run in reverse registration order, but still gathered).

 

Hooks can only be added before the server is up. Trying to register one after startup raises, which is the framework's way of catching a mistake early. The practical rule: register hooks while building your app (in the factory), not from inside a request.

Graceful shutdown

The runtime installs handlers for SIGINT and SIGTERM, so a Ctrl+C or a container stop flips the shutdown flag instead of killing the process mid-request. The in-flight request finishes, your shutdown hooks run, and only then does the server stop — which is why you'll always see the "Running shutdown hooks..." line before the app exits.

Continue reading

From here you can continue straight with Runtime.