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.app import app_router
from fluid.api import api_router
app.include_router(app_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("The app 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 run one after another in the order you register them, and shutdown hooks in reverse registration order. Later hooks can rely on earlier ones having finished — but a hook that blocks also holds up every hook queued after it, so keep them focused and let long running work happen elsewhere.
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.