Initializers
Initializers are similar
to 
Middleware -- they both
allow configuring the Axum Router for your app's HTTP service.
However, Initializers provide more precise control over when it is applied to the Router. This is useful to apply
middleware that requires a fully set up Router in order to work as expected, e.g. Tower's 
NormalizePathLayer. It's
also useful in order to initialize Extensions that you
want to attach to the Router -- this is most useful for using external utility crates that expect state to be in an
Extension, most state you need in your own application code should probably be in a custom state struct.
Initializer hooks
Initializers have various hooks to allow modifying the Router at a particular point in the process of building it.
They are listed below in the order in which they are run:
- after_router: Runs after all of the routes have been added to the- Router
- before_middleware: Runs before any- Middlewareis added to the- Router.
- after_middleware: Runs after all- Middlewarehas been added to the- Router.
- before_serve: Runs right before the HTTP service starts.
Default initializers
Currently, the only Initializer provided by Roadster is the [NormalizePathInitializer]. This initializer applies
Tower's 
NormalizePathLayer after
all other Router setup has completed, which is required in order for it to properly normalize paths (e.g., treat paths
with and without trailing slashes as the same path).
If more Initializers are added in the future, they can be found in
the module's docs.rs page.
All of the default initializers can be configured via the app's config files. All initializers have at least the following config fields:
- enable: Whether the initializer is enabled. If not provided, the initializer enablement falls back to the value of the- service.http.initializer.default-enablefield.
- priority: The priority in which the initializer will run. Lower values (including negative numbers) run before higher values. The middlewares provided by Roadster have priorities between- -10,000(runs first) and- 10,000(runs later) by default, though these values can be overridden via configs. If the order your initializer runs in relative to other initializers doesn't matter, simply set to- 0.
Custom initializers
Custom initializers can be provided by implementing the 
Initializer trait. As a
convenience, custom initializers can also be applied using the 
AnyInitializer
utility. This is useful to run an initializer without adding a full struct + trait implementation.
const BASE: &str = "/api";
/// Set up the [`HttpServiceBuilder`]. This will then be registered with the
/// [`roadster::service::registry::ServiceRegistry`].
pub async fn http_service(state: &AppContext) -> RoadsterResult<HttpServiceBuilder<AppContext>> {
    HttpServiceBuilder::new(Some(BASE), state)
        .api_router(ApiRouter::new().api_route(
            &build_path(BASE, "/example_b"),
            get_with(example_b::example_b_get, example_b::example_b_get_docs),
        ))
        .initializer(
            AnyInitializer::builder()
                .name("custom-initializer")
                .apply(|router, _state| {
                    info!("Running custom initializer");
                    Ok(router)
                })
                .build(),
        )
}