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

One reading per request

The engine's generated current() carries one warning: call it once per request and reuse the value — a reload landing between two calls would otherwise let one request observe two configurations. With one section that is easy advice. With two it is impossible advice, because "the same generation" is a property of a pair of reads that no single call site can see:

async fn handler() -> String {
    let server = ServerConfig::current();     // generation 7
    // a reload lands here
    let features = FeaturesConfig::current(); // generation 8
    // this response now mixes two documents
}

Both reads are correct. The response is not.

These five crates turn the advice into something the type system arranges: a layer takes one snapshot when the request begins, and every extractor in the handler reads out of that snapshot.

[dependencies]
dynamic-config-axum = "<version>"
async fn handler(
    Config(server): Config<ServerConfig>,
    Config(features): Config<FeaturesConfig>,
) -> String {
    // One reading. These came from one snapshot.
    format!("{} {}", server.port(), features.cache())
}

The five crates

CrateWhat it isMSRV
dynamic-config-web-corethe snapshot and the section list — no framework1.71
dynamic-config-towerthe layer/service pair over any tower stack1.71
dynamic-config-axumthe tower layer re-exported + a FromRequestParts extractor1.80
dynamic-config-actixthe same two pieces through Actix's Transform/FromRequest1.88
dynamic-config-locothe Initializer Loco asks a library for, over the axum crate1.94

What they are not

They own no lifecycle. Loading, watching and the WatchHandle stay in your main, exactly where the engine's book puts them. They ship no routes: status(), check() and Exposition are public engine surface, and Production Surface shows that a handler over them is shorter than a route surface would be to adopt. They add nothing to the engine — everything here is closures over API that already exists.

The engine, the stores, and the Python and Node ecosystems each have their own book — the family page is the map. The Python web package makes the opposite choice to this one (it ships routes and a lifecycle), for a reason that does not apply here: a Python service has no tower to compose with.