Skip to content

Post-processing

Post-processing middlewares are invoked after the request handler.
They are suitable for modifying the response and/or performing side-effects based on its contents. E.g. logging the response's status code, injecting response headers, etc.

src/core/mw.rs
use pavex::{response::Response};
use pavex_tracing::{
    RootSpan,
    fields::{http_response_status_code, HTTP_RESPONSE_STATUS_CODE}
};

pub fn response_logger(response: Response, root_span: &RootSpan) -> Response
{
    root_span.record(
        HTTP_RESPONSE_STATUS_CODE,
        http_response_status_code(&response),
    );
    response
}

Registration

You register a post-processing middleware against a blueprint via the post_process method.

src/core/blueprint.rs
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;

pub fn blueprint() -> Blueprint {
    let mut bp = Blueprint::new();
    bp.post_process(f!(super::response_logger));
    bp.route(GET, "/", f!(super::handler));
    bp
}

You must provide an unambiguous path to the middleware, wrapped in the f! macro.

The middleware will be invoked for all request handlers registered after it, as long as they were registered against the same Blueprint or one of its nested children. Check out the scoping section for more details.

Registration syntax

You can use free functions, static methods, non-static methods, and trait methods as middlewares. Check out the dependency injection cookbook for more details on the syntax for each case.

IntoResponse

Post-processing middlewares, like request handlers, must return a type that can be converted into a Response via the IntoResponse trait.
If you want to return a custom type from your middleware, you must implement IntoResponse for it.

Middlewares can fail

Post-processing middlewares can be fallible, i.e. they can return a Result.

src/fallible/mw.rs
use pavex::response::Response;
use super::CompressionError;

pub fn compress(response: Response) -> Result<Response, CompressionError>
{
    let compressed = {
        // Try to compress the response
        // [...]
    }?;
    Ok(compressed)
}

If they do, you must specify an error handler when registering them:

src/fallible/blueprint.rs
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;

pub fn blueprint() -> Blueprint {
    let mut bp = Blueprint::new();
    bp.post_process(f!(super::compress))
        .error_handler(f!(super::compression_error_handler));
        // [...]
    bp
}

Check out the error handling guide for more details.

Dependency injection

Post-processing middlewares can take advantage of dependency injection.

You must specify the dependencies of your middleware as input parameters in its function signature.
Those inputs are going to be built and injected by the framework, according to the constructors you have registered.

Post-processing middlewares, like request handlers and pre-processing middlewares, can mutate request-scoped types. Ask for a &mut reference to the type you want to mutate as an input parameter, the framework will take care of the rest.

Check out the dependency injection guide for more details on how the process works.
Check out the request data guide for an overview of the data you can extract from the request using Pavex's first-party extractors.