Skip to content

Post-processing

Post-processing middlewares are invoked after the route handler.
They can modify the response and/or perform side-effects based on its contents. E.g. logging the response's status code, injecting response headers, etc.

Defining post-processing middlewares

Use the #[post_process] attribute to define a new post-processing middleware:

use pavex::Response;
use pavex::post_process;
use pavex_tracing::{
    RootSpan,
    fields::{HTTP_RESPONSE_STATUS_CODE, http_response_status_code},
};

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

Signature

Pavex accepts a wide range of function signatures for post-processing middlewares. There are two constraints:

Other than that, you have a lot of freedom in how you define your post-processing middlewares:

Registration

Invoke Blueprint::post_process to register a post-processing middleware:

use crate::logger::RESPONSE_LOGGER;
use pavex::{Blueprint, blueprint::from};

pub fn blueprint() -> Blueprint {
    let mut bp = Blueprint::new();
    bp.post_process(RESPONSE_LOGGER); // (1)!
    // [...]
}
  1. RESPONSE_LOGGER is a strongly-typed constant generated by the #[post_process] attribute on the response_logger function.
    Check out the documentation on component ids for more details.

The middleware will be invoked for all routes registered after it. Check out the scoping section for more details.

Middlewares can fail

Post-processing middlewares can return a Result.

use crate::errors::CompressionError;
use pavex::Response;
use pavex::post_process;

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

Check out the error handling guide for more details on how to handle the error case.

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.