Skip to content

Middleware and Errors

Spry keeps request behavior in two explicit places:

  • middleware for cross-cutting flow control
  • error handlers for translating failures into responses

Global middleware

Files in top-level middleware/ are loaded in filename order.

dart
// ignore_for_file: file_names

import 'package:spry/spry.dart';

Future<Response> middleware(Event event, Next next) async {
  final startedAt = DateTime.now();
  final response = await next();
  final duration = DateTime.now().difference(startedAt).inMilliseconds;
  print('${event.request.method} ${event.request.url.path} -> ${response.status} (${duration}ms)');
  return response;
}

This is the right place for:

  • request logging
  • tracing
  • auth shells
  • response timing

Scoped middleware

Use _middleware.dart inside routes/ when behavior should apply only to that branch of the route tree.

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

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

This is useful when a subset of routes needs shared locals, auth checks, or response wrapping.

Error handling

Use _error.dart to catch errors inside the current route scope and convert them into a stable response shape.

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

Response onError(Object error, StackTrace stackTrace, Event event) {
  if (error case NotFoundError()) {
    return Response.json({
      'error': 'not_found',
      'path': event.request.url.path,
    }, status: 404);
  }
  if (error case HTTPError()) {
    return error.toResponse();
  }

  return Response.json({
    'error': 'internal_server_error',
    'path': event.request.url.path,
  }, status: 500);
}

This is the clean path for:

  • handling NotFoundError
  • returning structured JSON errors
  • avoiding repeated try/catch blocks in handlers

Practical rule

  • if it changes request flow across multiple routes, use middleware
  • if it converts thrown errors into responses, use _error.dart
  • if it belongs to one handler only, keep it inside that handler

Released under the MIT License.