Expand description
Shared Axum middleware, HTTP telemetry, and public errors for Baukit services.
finalize applies one consistent request lifecycle: standard extractor and
routing errors, request identity, W3C trace extraction and response
propagation, route-template spans, HTTP RED metrics, panic and timeout
envelopes, body and concurrency limits, and explicit CORS.
§Example
use axum::{Router, routing::get};
use baukit_http::{HttpOptions, RequestId, finalize};
async fn hello(request_id: RequestId) -> String {
format!("hello from request {}", request_id.as_str())
}
let options = HttpOptions::default()
.with_allowed_origins(["https://app.example.com"])?
.with_additional_allowed_headers(["accept", "x-webhook-secret"])?
.with_json_rejection_code("invalid_json")?;
let app = finalize(Router::new().route("/hello", get(hello)), options);classify_http_status turns an upstream response into a RetryClass so
outbound clients share one retry policy.
§Paginated handler
Keyset pagination lives in baukit_core::pagination so domain crates can
use it without Axum. PaginationError converts into ApiError.
use axum::extract::Query;
use baukit_core::pagination::{Page, PageKey, PageParams};
use baukit_http::{ApiError, ResponseEnvelope};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Deserialize)]
struct ListQuery {
limit: Option<i64>,
cursor: Option<String>,
category: Option<String>,
}
#[derive(Serialize)]
struct Filters {
category: Option<String>,
}
#[derive(Clone, Serialize)]
struct Item {
id: Uuid,
name: String,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct PageMeta {
next_cursor: Option<String>,
}
async fn list(
Query(query): Query<ListQuery>,
) -> Result<ResponseEnvelope<Vec<Item>, PageMeta>, ApiError> {
let params = PageParams::new(query.limit, query.cursor)?;
let filters = Filters { category: query.category };
let after = params.decode_cursor(&filters)?;
// Fetch `params.fetch_limit()?` rows ordered by (name, id), starting
// after `after.page_key::<String>()?` when it is present.
let _ = after;
let rows: Vec<Item> = Vec::new();
let page = Page::from_rows(rows, ¶ms, &filters, |item| {
PageKey::new(item.name.clone(), item.id)
})?;
Ok(ResponseEnvelope::new(
page.items,
PageMeta { next_cursor: page.next_cursor },
))
}This crate records HTTP metrics through the metrics facade and never
installs a recorder. The recorder owner must configure
http_request_duration_seconds with DURATION_BUCKETS.
Re-exports§
pub use retry::RetryClass;pub use retry::RetryHeaderOptions;pub use retry::classify_http_status;pub use retry::classify_http_status_with_options;pub use retry::classify_http_status_with_options_at;pub use retry::classify_transport_error;pub use retry::retry_after_from_headers;pub use retry::retry_after_from_headers_at;pub use retry::retry_after_from_headers_with_options;pub use retry::retry_after_from_headers_with_options_at;pub use metrics;
Modules§
- retry
- Classification of upstream HTTP responses and transport failures.
Structs§
- ApiError
- A safe public API error rendered in Baukit’s standard JSON envelope.
- ApiJson
- A JSON body extractor whose rejections use Baukit’s standard error envelope.
- ApiPath
- Backward-compatible name for
Path. Backward-compatible name forPath. Backward-compatible name forPath. A route-path extractor whose rejections use Baukit’s standard error envelope. - ApiQuery
- Backward-compatible name for
Query. Backward-compatible name forQuery. Backward-compatible name forQuery. A query-string extractor whose rejections use Baukit’s standard error envelope. - Error
Body - The error body nested inside
ErrorEnvelope. - Error
Envelope - The shared public error response envelope.
- Http
Options - Configuration for the shared HTTP layer stack.
- Idempotency
Key - A validated
Idempotency-Keyvalue. - Idempotency
KeyRule - Length bounds for
Idempotency-Keyvalues on one route. - Invalid
Etag Prefix - An ETag prefix is longer than
MAX_ETAG_PREFIX_BYTESor has a byte outside[A-Za-z0-9._:-]. - Invalid
Idempotency KeyRule - Bounds outside
1 <= min_bytes <= max_bytes <= MAX_IDEMPOTENCY_KEY_BYTES. - Json
Rejection Codes - Stable error codes for each JSON extractor rejection class.
- Path
- A route-path extractor whose rejections use Baukit’s standard error envelope.
- Query
- A query-string extractor whose rejections use Baukit’s standard error envelope.
- Request
Id - A request identifier accepted from the client or generated as a UUID.
- Request
Locale - A locale selected from the configured supported set for one request.
- Request
Locale Config - Product-owned locale choices used by
RequestLocale. - Response
Envelope - A fully typed success-response envelope.
- Revision
- A non-negative resource revision carried in a strong ETag.
- Revision
Etag - Formats and parses strong revision ETags of the form
"<prefix><revision>". - Revision
OutOf Range - A value outside
0..=i64::MAXcannot be aRevision. - Rfc3339
Date Time - An API timestamp with one JSON and OpenAPI representation: an RFC 3339
string with OpenAPI
date-timeformat.
Enums§
- Http
Options Error - An invalid shared HTTP layer option.
- Idempotency
Error - A keyed mutation that cannot run or replay.
- Invalid
Idempotency Key - Why an
Idempotency-Keyvalue is outside the route’s grammar. - Invalid
IfMatch - Why an
If-Matchvalue is not one strong ETag for the resource. - Locale
Query Override - Controls whether a decoded query parameter overrides
Accept-Language. - Precondition
Error - A failed revision precondition.
- Request
Locale Config Error - Invalid request-locale configuration.
- Request
Locale Rejection - Predictable malformed-input failures from
RequestLocale. - Response
Cache Policy - Whether the HTTP layers add a default
Cache-Controlheader to responses.
Constants§
- DURATION_
BUCKETS - Required Prometheus buckets for
HTTP_REQUEST_DURATION_SECONDS, in seconds. - HTTP_
REQUESTS_ IN_ FLIGHT - Gauge metric for active HTTP requests, labeled
methodandroute. - HTTP_
REQUESTS_ TOTAL - Counter metric for completed HTTP requests, labeled
method,route, andstatus. - HTTP_
REQUEST_ DURATION_ SECONDS - Histogram metric for HTTP request seconds, labeled
method,route, andstatus. - IDEMPOTENCY_
KEY - The
Idempotency-Keyrequest header. - IDEMPOTENCY_
KEY_ IN_ PROGRESS_ CODE - Error code for a key whose first request has not finished.
- IDEMPOTENCY_
KEY_ REQUIRED_ CODE - Error code for a route that requires
Idempotency-Keyand received none. - IDEMPOTENCY_
KEY_ REUSED_ CODE - Error code for a key already used in the same scope with a different request.
- INVALID_
IDEMPOTENCY_ KEY_ CODE - Error code for an
Idempotency-Keyvalue outside the route’s grammar. - MAX_
ACCEPT_ LANGUAGE_ BYTES - Maximum combined
Accept-Languageheader length inspected byRequestLocale. - MAX_
ETAG_ PREFIX_ BYTES - The longest prefix a
RevisionEtagaccepts, in bytes. - MAX_
IDEMPOTENCY_ KEY_ BYTES - The longest key any
IdempotencyKeyRulemay accept, in bytes. - MAX_
LOCALE_ QUERY_ BYTES - Maximum raw query length inspected by
RequestLocale. - MAX_
SUPPORTED_ LOCALES - Maximum number of locales in a
RequestLocaleConfig. - X_
REQUEST_ ID - The standard request ID header.
Functions§
- ensure_
current_ revision - Compares the revision a client sent with the stored one.
- extract_
trace_ context - Extracts W3C trace context from request headers using the global propagator.
- finalize
- Finalizes a public router with standard 404/405 envelopes and HTTP layers.
- inject_
current_ trace_ context - Injects the current tracing span’s OpenTelemetry context into outbound headers.
- inject_
trace_ context - Injects
contextinto outbound headers using the global W3C propagator. - layers
- Applies the complete Baukit HTTP defaults stack to an Axum router.