LogoRevali

Request Wrapper

Wrap the entire request pipeline in setup and teardown logic

A RequestWrapper is a Lifecycle Component that wraps the entire request pipeline. It runs setup logic before the rest of the lifecycle executes, calls next() to continue the pipeline, and runs teardown logic after the pipeline completes.

Request wrappers are useful when you need to establish a scope that spans middleware, guards, interceptors, and the endpoint — for example, installing a request-scoped DI container or propagating values through a Zone.

Execution#

Request wrappers are the outermost Lifecycle Component. They run before observers, middleware, guards, and interceptors.

When multiple request wrappers are registered, they are nested like middleware: the first registered wrapper runs its setup first and its teardown last.

Wrapper A (pre)
  Wrapper B (pre)
    Observer (pre)
    MiddlewareGuardInterceptor (pre)EndpointInterceptor (post)
  Wrapper B (post)
Wrapper A (post)
Observer (post)

Create a Request Wrapper#

To create a RequestWrapper, implement the RequestWrapper class and implement the wrap method.

lib/components/wrappers/my_wrapper.dart
import 'package:revali_router/revali_router.dart';

class MyWrapper implements RequestWrapper {
  const MyWrapper();

  @override
  Future<Response> wrap(Context context, NextResponse next) async {
    // Setup before the pipeline runs
    try {
      return await next();
    } finally {
      // Teardown after the pipeline completes
    }
  }
}

The next callback continues the execute pipeline: middleware → guards → interceptors → handler. Always invoke next() unless you intend to short-circuit the request and return a response directly.

As a Lifecycle Component#

You can also define a request wrapper as a method on a Lifecycle Component class. The method must return WrapperResult (or Future<Response>) and accept a NextResponse parameter.

lib/components/request_scope.dart
import 'package:revali_router/revali_router.dart';

class RequestScope implements LifecycleComponent {
  const RequestScope();

  WrapperResult wrap(NextResponse next, DI parentDi) {
    final scoped = RequestScopedDI(parent: parentDi);

    return runZoned(
      () async {
        try {
          return await next();
        } finally {
          await scoped.dispose();
        }
      },
      zoneValues: {RequestScopedDI.zoneKey: scoped},
    );
  }
}

Downstream lifecycle components and endpoints can then resolve dependencies from the request scope:

final userService = RequestScopedDI.getFrom<UserService>(appDi);

Register the Request Wrapper#

To register the RequestWrapper, annotate your class on the app, controller, or endpoint level.

routes/controllers/my_controller.dart
import 'package:revali_router/revali_router.dart';

// highlight-next-line
@RequestScope()
@Get('')
Future<void> myEndpoint() {
    ...
}

Register as Type Reference#

routes/controllers/my_controller.dart
import 'package:revali_router/revali_router.dart';

// highlight-next-line
@Wrappers([RequestScope])
@Get('')
Future<void> myEndpoint() {
    ...
}