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

Quick Start

[dependencies]
dynamic-config = { version = "<version>", features = ["toml", "watch"] }
dynamic-config-axum = "<version>"
use std::time::Duration;

use axum::{routing::get, Router};
use dynamic_config::dynamic_config;
use dynamic_config_axum::{sections, Config, SnapshotLayer};
use serde::Deserialize;

#[dynamic_config]
#[derive(Deserialize)]
struct Server {
    host: String,
    port: u16,
}

#[dynamic_config]
#[derive(Deserialize)]
struct Features {
    cache: bool,
}

async fn index(
    Config(server): Config<Server>,
    Config(features): Config<Features>,
) -> String {
    format!("{}:{} cache={}", server.host, server.port, features.cache)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // The lifecycle is yours, unchanged: load before serving, watch after.
    Server::builder("server").file("config.toml").init()?;
    Features::builder("features").file("config.toml").init()?;

    let _watchers = [
        Server::builder("server")
            .file("config.toml")
            .watch(Duration::from_millis(250))?,
        Features::builder("features")
            .file("config.toml")
            .watch(Duration::from_millis(250))?,
    ];

    let app = Router::new()
        .route("/", get(index))
        // After the routes it covers, like any tower layer.
        .layer(SnapshotLayer::new(sections![Server, Features]));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
    axum::serve(listener, app).await?;

    Ok(())
}

Edit config.toml while it serves. The next request answers with the new document; no request ever mixes two. That is the whole product — One Reading per Request is what it promises precisely, and each framework chapter is the same wiring through different seams.

Two mistakes the crates catch for you, loudly:

  • A handler asking for a section the layer was not given gets a 500 naming the type and the fix — a wiring bug, not a client error.
  • .layer() before .route() compiles and 500s on every request — the axum chapter says why, and the error text points here.

Runnable versions of exactly this: cargo run -p dynamic-config-axum --example axum_two_sections, and siblings for Actix and Loco.