Skip to main content

Crate axum_api_kit

Crate axum_api_kit 

Source
Expand description

Shared response types for Axum JSON APIs.

Provides building blocks that every Axum CRUD service needs but always re-defines from scratch:

  • ApiError - a machine-readable JSON error body with code, message, and optional details, plus factory helpers that return (StatusCode, Json<ApiError>) tuples ready for use with Axum’s IntoResponse. The too_many_requests_with_retry_after and service_unavailable_with_retry_after factories additionally emit a delay-seconds Retry-After header. Supports From conversions for common error types. With the optional validator feature enabled, also supports converting validator::ValidationErrors into structured field errors. With the optional sqlx feature enabled, also supports converting sqlx::Error into semantically correct HTTP status codes (404, 409, 422, 503, 500).
  • ListResponse<T> - a generic offset/limit paginated collection response with data, total, limit, and offset fields.
  • CursorResponse<T> - a generic cursor-based paginated collection response for large datasets or feeds, with data, next_cursor, and has_more fields.
  • HealthResponse - a health-check response with status field supporting ok, degraded, and unhealthy states.
  • Created<T>, Accepted<T>, and NoContent - success-side responses for the rest of the CRUD lifecycle: 201 Created (with an optional Location header), 202 Accepted, and 204 No Content.

With optional feature flags enabled, the kit also provides request extractors that reject with an ApiError body on failure:

  • ValidatedJson<T> (feature validator) - deserializes a JSON body and runs validator validation before the handler runs.
  • Pagination and CursorPagination (feature extract) - parse limit/offset and cursor/limit query parameters into typed values, with list_response / cursor_response helpers that build the matching response type.
  • ApiJson<T> (feature extract) - a drop-in replacement for axum::Json whose extraction failures reject with an ApiError body instead of Axum’s plain-text default.

It also ships observability middleware:

  • propagate_request_id and trace_requests (feature trace) - assign an x-request-id correlation id (extractable via RequestId) and emit a structured tracing event with method, path, status, and latency for each request.

And service-wiring helpers:

  • health_routes and liveness (feature router) - a Router exposing /healthz and /readyz probes backed by HealthResponse.
  • cors_allowing and cors_permissive (feature cors) - build a tower_http CorsLayer with sensible defaults.

And an RFC 9457 error format:

  • Problem (feature problem) - an RFC 9457 problem+json response type with builder methods, lossless bridges from ApiError, and Retry-After support. Emits Content-Type: application/problem+json, with opt-in Accept-header content negotiation (the ProblemFormat extractor plus Problem::into_response_for / Problem::into_response_with) that serves the same body as plain application/json only when the client strictly prefers it.
  • ProblemJson<T> (features problem + extract) and ProblemValidatedJson<T> (features problem + validator) - problem-flavored siblings of ApiJson / ValidatedJson whose extraction failures reject with an RFC 9457 body via ProblemRejection (same status codes, same code and field-level details, Content-Type negotiated via ProblemFormat). The existing extractors’ rejection bodies never change; the format is chosen by naming the extractor in the handler.

With the openapi feature, all four response types derive utoipa::ToSchema so they can be referenced from a utoipa OpenApi document and appear in generated specs.

§Quick Start

use axum::{Json, http::StatusCode, response::IntoResponse};
use axum_api_kit::{ApiError, ListResponse, CursorResponse, HealthResponse};
use serde::Serialize;

#[derive(Serialize)]
struct Item { id: String }

async fn list_items() -> impl IntoResponse {
    let items = vec![Item { id: "1".into() }];
    Json(ListResponse { data: items, total: 1, limit: 50, offset: 0 })
}

async fn feed_items(cursor: Option<String>) -> impl IntoResponse {
    let items = vec![Item { id: "1".into() }];
    CursorResponse { data: items, next_cursor: Some("abc".into()), has_more: true }
}

async fn get_item() -> impl IntoResponse {
    ApiError::not_found("item not found")
}

async fn health() -> impl IntoResponse {
    HealthResponse::ok()
}

Structs§

Accepted
A 202 Accepted response carrying a JSON body.
ApiError
A machine-readable JSON error body.
ApiJson
A drop-in replacement for axum::Json whose extraction failures reject with an ApiError JSON body instead of Axum’s default plain-text response.
Created
A 201 Created response carrying the newly created resource as a JSON body.
CursorPagination
Cursor-based pagination parameters parsed from the query string.
CursorResponse
A cursor-based paginated collection response.
HealthResponse
A minimal health-check response body.
ListResponse
A generic paginated collection response.
NoContent
A 204 No Content response with an empty body.
Pagination
Offset/limit pagination parameters parsed from the query string.
Problem
An RFC 9457 problem details response body.
ProblemJson
The problem-flavored sibling of ApiJson: deserializes a JSON request body exactly like axum::Json, but extraction failures reject with an RFC 9457 Problem body via ProblemRejection.
ProblemRejection
The rejection produced by ProblemJson and ProblemValidatedJson: an RFC 9457 Problem plus the ProblemFormat negotiated from the request’s Accept headers.
ProblemValidatedJson
The problem-flavored sibling of ValidatedJson: deserializes a JSON request body, validates it with validator, and rejects with an RFC 9457 Problem body via ProblemRejection on failure.
RequestId
A request correlation id, stored in request extensions by propagate_request_id.
ValidatedJson
An Axum extractor that deserializes a JSON request body and validates it with validator.

Enums§

ProblemFormat
The media type a Problem response is served as: the RFC 9457 default application/problem+json, or plain application/json for clients that explicitly prefer it.

Constants§

APPLICATION_PROBLEM_JSON
The application/problem+json media type from RFC 9457.
REQUEST_ID_HEADER
The header used to carry the request correlation id: x-request-id.

Functions§

cors_allowing
Build a CORS layer that allows the given origins.
cors_permissive
A permissive CORS layer (any origin, method, and header).
health_routes
Build a Router exposing /healthz (liveness) and /readyz (readiness) probes.
liveness
Liveness probe handler: always returns HealthResponse::ok.
propagate_request_id
Axum middleware that assigns a request correlation id and echoes it on the response.
trace_requests
Axum middleware that emits a structured tracing event when each request completes.