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:
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.