Lifecycle
Spry is easier to reason about when you know the request order.
Request flow
For an incoming request, Spry works through these layers:
- check
public/for matchingGETorHEADassets - match the route handler from
routes/ - create the request-scoped
Event - run global and scoped middleware
- run the matched handler or fallback
- if something throws, run scoped
_error.darthandlers
This is the part people actually care about during development: where behavior can intercept a request.
For websocket routes, the handshake still follows this flow. After the runtime commits the websocket upgrade, the session leaves the normal HTTP middleware and error pipeline. That means _error.dart can still shape handshake failures, but not post-upgrade websocket session exceptions. See WebSockets.
Lifecycle hooks
Use hooks.dart for process-level lifecycle work:
dart
import 'package:spry/spry.dart';
Future<void> onStart(ServerLifecycleContext context) async {
print('Spry dart_vm example started: ${context.runtime.name}');
}This is a good place for:
- startup logging
- runtime warm-up
- teardown work
Mental model
Middleware shapes request flow. Hooks shape process lifecycle. Route handlers shape the response.