crates.io ↗

iet-http-axum

feature adapter official

Turns HTTP requests into calls on your application and maps results back to JSON responses.

CoreFeaturesIntegrationsStacksTooling
Used byiet-auth-basic-axumiet-auth-login-htmx-axumiet-auth-profiles-axumiet-auth-session-axumiet-stack-auth-htmx-axumiet-stack-web-axum
iet-http-axumadapterREST/JSON transport adapter
Depends oniet-telemetrycontractiet-rate-limit-corecontractiet-corecontractiet-appcontractiet-configcontractiet-config-macroscontract

How to use it

iet-http-axum gives an Axum application the REST transport conventions: a RestApi builder that sets the body limit and merges named modules, extractors that turn a rejected path, query, or JSON body into an application/problem+json response before your handler runs, and an HttpHost that owns the listener and drain. Handlers return ordinary Axum responses, so it composes with any other Axum routes.

rust
// crates/features/iet-http-axum/tests/rest.rs
use iet_http_axum::{ApiError, Created, RestApi, ValidatedJson};

#[derive(serde::Deserialize, serde::Serialize)]
#[serde(deny_unknown_fields)]
struct Input { name: String }

// impl iet_core::Validate for Input { /* reject empty name with a Validation AppError */ }

async fn create(ValidatedJson(input): ValidatedJson<Input>) -> Created<Input> {
    // `Input: iet_core::Validate`; a malformed or empty body is rejected
    // before this function runs, as `application/problem+json`.
    Created(input)
}

fn router() -> axum::Router {
    RestApi::new()
        .route("/items", axum::routing::post(create))
        .body_limit(64)
        .unwrap()
        .finish(())
}
  • ValidatedQuery<T> and ApiPath<T> apply the same validation/rejection rules to query strings and path parameters.
  • RestApi::module / module_with_policy merge a sub-router under a name; the latter validates the shared rate-limit policy at registration time.
  • To serve it, bind HttpHost::bind(HostConfig { address, ..Default::default() }, router).await? and call host.run(shutdown).await? (see crates/features/iet-http-axum/tests/host.rs).

How it works

RestApi builds one Axum Router: routes may be wrapped by a rate limiter, modules are merged by name, and finish adds the body limit, telemetry instrumentation, and not-found/method-not-allowed fallbacks. HttpHost::bind adds a listener-level body limit and instrumentation, then run serves with graceful shutdown and drains within the configured timeout. Extractors such as ValidatedJson validate before the handler and convert a rejection into an application/problem+json ApiError.

sequenceDiagram
  participant C as "Client"
  participant H as "HttpHost"
  participant X as "ValidatedJson"
  participant R as "Handler"
  C->>H: POST /items
  H->>H: body limit and instrument
  H->>X: extract JSON body
  X->>X: Validate::validate
  alt valid
    X->>R: Input
    R-->>C: Created 201
  else rejected
    X-->>C: 422 problem+json
  end

What it implements

  • Implements iet_app::Module from iet-app.
  • Implements iet_app::Worker from iet-app.

What's inside

Modules

  • src/compression.rs
  • src/config.rs
  • src/credentials.rs
  • src/error.rs
  • src/extract.rs
  • src/health.rs
  • src/host.rs
  • src/lib.rs
  • src/rate_limit.rs
  • src/routes.rs
  • src/telemetry.rs

Public API

(root)

  • struct NoStore — Mark private responses, including errors, as unsuitable for shared storage.

compression

  • struct NoCompression
  • struct CompressionPredicate
  • pub fn layer(min_bytes: u16, level: u32) -> AppResult<CompressionLayer<CompressionPredicate>> — Construct a layer with a minimum known body size and gzip level (1–9). Unknown-size bodies stream without buffering. SSE, private/no-transform, binary, and explicitly opted-out responses are excluded.

config

  • struct HostConfig
  • pub fn from_source(source: &impl iet_config::ConfigSource) -> AppResult<Self>
  • pub fn validate(&self) -> AppResult<()>

credentials

  • pub fn bearer_token(headers: &HeaderMap) -> AppResult<String> — Parse exactly one bounded Authorization header; never accept URL credentials. The returned value is secret material: callers must not log it.

error

  • pub fn error_status(kind: ErrorKind) -> StatusCode — Shared transport classification; presentation must not expose diagnostics.
  • struct ApiError
  • struct Created
  • struct NoContent

extract

  • struct ApiPath — Typed path rejection without reflecting the submitted value into responses.
  • struct ValidatedQuery — Parse and validate query DTOs; use bounded pagination types in Validate.
  • struct ValidatedJson — Transport validation is defense-in-depth; direct service calls must validate too.

health

  • pub fn router(health: Health, service: impl Into<String>, max_age: Duration) -> Router

host

  • struct HttpHost
  • pub fn address(&self) -> AppResult<SocketAddr>
  • struct HttpServer — Inject an explicitly composed router; configuration is loaded before binding.

rate_limit

  • trait RateLimiterExt — Apply once after composing a router group. All routes share the policy budget.
rust
pub trait RateLimiterExt: Sized {
    fn by_ip(
        self,
    ) -> impl Layer<
        axum::routing::Route,
        Service = impl Service<Request, Response = Response, Error = Infallible, Future = impl Send>
                  + Clone
                  + Send
                  + Sync
                  + 'static,
    > + Clone
    + Send
    + Sync
    + 'static;

    /// Authentication must install Principal before this layer runs.
    fn by_principal(
        self,
    ) -> impl Layer<
        axum::routing::Route,
        Service = impl Service<Request, Response = Response, Error = Infallible, Future = impl Send>
                  + Clone
                  + Send
                  + Sync
                  + 'static,
    > + Clone
    + Send
    + Sync
    + 'static;
}
  • struct RateLimit
  • trait Subject — Statically dispatched identity selection; values may borrow from the request.
rust
pub trait Subject: Clone + Send + Sync + 'static {
    fn subject<'a>(&self, request: &'a Request) -> Option<impl Display + Send + 'a>;
}
  • struct PeerIp
  • struct AuthenticatedPrincipal

routes

  • struct RestApi — Concrete state shared by registered transport modules. No service locator.
  • pub fn new() -> Self
  • pub fn rate_limit<N: RateLimit>(self, limiter: N) -> RestApi<S, N> — Select once before registering routes; integration modules inherit it.
  • pub fn rate_limits_enabled(mut self, enabled: bool) -> Self — Enable or disable all rate-limit layers registered through this stack. Enabled by default; disabling is intended for controlled environments such as isolated capacity tests.
  • pub fn rate_policy(mut self, name: &str, config: RateLimitConfig) -> AppResult<Self> — Override a named integration policy before registering that integration.
  • pub fn route(mut self, path: &str, methods: MethodRouter<S>) -> Self — Use a single MethodRouter per path to keep method registration explicit. Axum rejects conflicting/malformed route definitions during construction.
  • pub fn module(self, name: &str, router: Router<S>) -> AppResult<Self>
  • pub fn module_with_policy( mut self, name: &str, router: Router<S>, default: RateLimitConfig, ) -> AppResult<Self>
  • pub fn body_limit(mut self, bytes: usize) -> AppResult<Self>
  • pub fn finish(self, state: S) -> Router

telemetry

  • pub fn inject_trace_context(headers: &mut axum::http::HeaderMap) — Explicit outbound propagation for custom HTTP clients. Baggage is excluded.

Impl map

  • Predicate for CompressionPredicate
  • Default for HostConfig
  • From<AppError> for ApiError
  • IntoResponse for ApiError
  • IntoResponse for Created<T>
  • IntoResponse for NoContent
  • axum::extract::FromRequestParts<S> for ApiPath<T>
  • axum::extract::FromRequestParts<S> for ValidatedQuery<T>
  • FromRequest<S> for ValidatedJson<T>
  • iet_app::Module for HttpServer
  • iet_app::Worker for HttpHost
  • axum::response::IntoResponse for NoStore<R>
  • RateLimiterExt for L
  • Display for ScopedSubject<T>
  • Subject for PeerIp
  • Subject for AuthenticatedPrincipal
  • Default for RestApi<S>
  • http_body::Body for ResponseBody
  • Drop for HttpTimer
  • Probe for Up
  • iet_config::ConfigSource for Source
  • Validate for Input
  • Validate for Page

Depends on

External crates: axum, http-body, opentelemetry, opentelemetry_sdk, serde, tokio, tower, tower-http, tracing, tracing-opentelemetry

Cargo features

  • default — (no dependencies)
  • compression — dep:tower-http, tower-http/compression-gzip
  • host — dep:iet-app, dep:iet-config, dep:iet-config-macros, dep:tokio, dep:tower-http, axum/tokio, axum/http1

Used by

In this repository

  • examples/handbook-notes
  • examples/htmx-starter
  • examples/reusable-notes
  • examples/skills-only-app
  • examples/skills-only-app-task-tracker

Source: crates/features/iet-http-axum