Skip to content

Middleware Overview

Spry keeps request behavior explicit.

Middleware is the layer for cross-cutting request logic such as:

  • request IDs
  • logging
  • response timing
  • header shaping
  • auth guards

If a concern changes request flow across multiple handlers, it belongs in middleware.

If it converts thrown errors into responses, it belongs in _error.dart.

The contract

Spry middleware uses one small contract:

dart
typedef Middleware = FutureOr<Response> Function(Event event, Next next);

That means a middleware can:

  • continue by calling next()
  • stop the pipeline by returning a Response

There is no extra hidden lifecycle around it.

Global and scoped middleware

Spry supports two places for middleware:

Global middleware

Files in top-level middleware/ apply across the app and are loaded in filename order.

text
middleware/
  01_request_id.dart
  02_logger.dart
  03_auth.get.dart

Spry also supports method-scoped global middleware. Add the HTTP method before .dart to apply a middleware only to that request method:

  • middleware/03_auth.get.dart
  • middleware/04_write_audit.post.dart

Supported method suffixes are: get, post, put, patch, delete, head, and options.

Scoped middleware

Use _middleware.dart inside routes/ when behavior should apply only to one route branch.

text
routes/
  admin/
    _middleware.dart
    _middleware.get.dart
    users.get.dart

_middleware.dart applies to every matching method in that scope. _middleware.get.dart applies only to GET requests in that scope.

Request-scoped state

Use event.locals when middleware needs to share data with downstream middleware or handlers.

dart
Future<Response> middleware(Event event, Next next) async {
  event.locals.set(#startedAt, DateTime.now());
  return next();
}

This is the preferred place for request-scoped values such as request IDs, timing markers, and authenticated principals.

First-party middleware

Spry exposes first-party middleware helpers from:

dart
import 'package:spry/middleware.dart';

Built-in middleware helpers currently include:

Creating your own middleware

Custom middleware should stay small and explicit:

dart
import 'package:spry/spry.dart';

Future<Response> middleware(Event event, Next next) async {
  final response = await next();
  response.headers.set('x-powered-by', 'spry');
  return response;
}

Use a first-party helper when the behavior is already covered.

Write your own middleware when the behavior is application-specific.

Released under the MIT License.