Goal
After Spry 8.1, the next step should be filling out the first-party middleware ecosystem rather than continuing with scattered optimizations.
The aim is to complement Spry's existing request pipeline with a coherent middleware layer while keeping the framework core small and explicit.
Scope
Principles
- Keep
package:spry focused on routing, generation, and runtime behavior.
- Prefer first-party middleware packages/helpers over expanding Spry core directly.
- Do not grow
Event with middleware-specific fields for this wave; prefer event.locals plus documented access patterns when request-scoped state is needed.
- Keep middleware behavior explicit and inspectable.
- Hold security-sensitive middleware (
auth, csrf) to a higher bar than cosmetic/header helpers.
Proposed rollout
Wave 1
requestId(...) (#177)
timing(...) (#178)
poweredBy(...)
cacheControl(...)
prettyJson(...)
every([...]) (#179)
except(...) (#179)
Why first:
- low-risk
- immediately useful
- no new control-flow contract required
Wave 2
cors(...)
etag(...)
language(...)
timeout(...)
Why second:
- still broadly useful
- needs more careful semantics around headers, negotiation, and response control
Wave 3
basicAuth(...)
bearerAuth(...)
csrf(...)
some(...) (#179)
Why last:
- security-sensitive
- likely to shape the eventual composition model
Design note on some(...)
some(...) ultimately landed as a general-purpose fallback combiner in #179, but with explicit semantics rather than a naive mirror of every(...).
Today, middleware is still:
typedef Middleware = FutureOr<Response> Function(Event event, Next next);
The some(...) helper resolves the missing generic "decline" state by treating:
- a normal return as success
- a thrown error as candidate failure
- shared
next() calls as a single downstream execution
When every candidate fails, some(...) throws a selected tracked error through SomeErrorThrower.
Expected outputs per middleware
Each middleware should eventually land with:
- focused unit tests
- route-level integration coverage
- one minimal example
- one short docs page or guide section
Non-goals for this wave
- websocket-specific middleware APIs
- websocket message-level middleware
- full session/auth frameworks
- full i18n frameworks
- full caching frameworks
The first job is to complete the common HTTP middleware layer around Spry's existing request pipeline.
Goal
After Spry 8.1, the next step should be filling out the first-party middleware ecosystem rather than continuing with scattered optimizations.
The aim is to complement Spry's existing request pipeline with a coherent middleware layer while keeping the framework core small and explicit.
Scope
some/every/except(#179)Principles
package:spryfocused on routing, generation, and runtime behavior.Eventwith middleware-specific fields for this wave; preferevent.localsplus documented access patterns when request-scoped state is needed.auth,csrf) to a higher bar than cosmetic/header helpers.Proposed rollout
Wave 1
requestId(...)(#177)timing(...)(#178)poweredBy(...)cacheControl(...)prettyJson(...)every([...])(#179)except(...)(#179)Why first:
Wave 2
cors(...)etag(...)language(...)timeout(...)Why second:
Wave 3
basicAuth(...)bearerAuth(...)csrf(...)some(...)(#179)Why last:
Design note on
some(...)some(...)ultimately landed as a general-purpose fallback combiner in #179, but with explicit semantics rather than a naive mirror ofevery(...).Today, middleware is still:
The
some(...)helper resolves the missing generic "decline" state by treating:next()calls as a single downstream executionWhen every candidate fails,
some(...)throws a selected tracked error throughSomeErrorThrower.Expected outputs per middleware
Each middleware should eventually land with:
Non-goals for this wave
The first job is to complete the common HTTP middleware layer around Spry's existing request pipeline.