Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

axum

[dependencies]
dynamic-config-axum = "<version>"

Two pieces: SnapshotLayer (the tower layer, re-exported from dynamic-config-tower) and Config<T> (a FromRequestParts extractor). Request extensions are axum's request scope, so there is no task-local and nothing to unwind.

let app = Router::new()
    .route("/", get(handler))
    .layer(SnapshotLayer::new(sections![Server, Features]));

Layer order is load-bearing

.layer() wraps only the routes present when it is called. This compiles, and answers 500 on every request:

let app = Router::new()
    .layer(SnapshotLayer::new(sections![Server]))   // wraps nothing
    .route("/", get(handler));                      // added after

The 500's Display names this page. Put the layer after the routes.

Nesting merges

An outer Router and a nested one may each carry a layer. The outer runs first; the inner one merges into what the outer took rather than replacing it, so a handler under both sees the union — inner wins on a type both list.

The escape hatch

snapshot(&Parts) answers the request's &Snapshot for code that is not an extractor — another middleware, a guard, a handler taking Request whole. SnapshotMissing::NoLayer is its error, and it means the layer did not run for this route.

Dynamic<T> instances

sections![A, B] expands to try_current/generation closures on the static slots. An instance-based Dynamic<T> registers by hand:

let sections = Sections::new()
    .section_with_generation(
        { let handle = handle.clone(); move || Some(handle.current()) },
        move || handle.generation(),
    );

Tests: dynamic-config-axum/tests/scope.rs asks the eight questions every adapter answers; axum_two_sections is the runnable example.