Skip to main content

axum_api_kit/
lib.rs

1//! Shared response types for Axum JSON APIs.
2//!
3//! Provides building blocks that every Axum CRUD service needs but always
4//! re-defines from scratch:
5//!
6//! - [`ApiError`] - a machine-readable JSON error body with `code`, `message`, and optional
7//!   `details`, plus factory helpers that return `(StatusCode, Json<ApiError>)` tuples ready
8//!   for use with Axum's [`IntoResponse`](axum::response::IntoResponse). Supports `From`
9//!   conversions for common error types. With the optional `validator` feature enabled,
10//!   also supports converting `validator::ValidationErrors` into structured field errors.
11//!   With the optional `sqlx` feature enabled, also supports converting `sqlx::Error` into
12//!   semantically correct HTTP status codes (404, 409, 422, 503, 500).
13//! - [`ListResponse<T>`] - a generic offset/limit paginated collection response with `data`,
14//!   `total`, `limit`, and `offset` fields.
15//! - [`CursorResponse<T>`] - a generic cursor-based paginated collection response for large
16//!   datasets or feeds, with `data`, `next_cursor`, and `has_more` fields.
17//! - [`HealthResponse`] - a health-check response with `status` field supporting `ok`,
18//!   `degraded`, and `unhealthy` states.
19//!
20//! With optional feature flags enabled, the kit also provides request extractors that
21//! reject with an [`ApiError`] body on failure:
22//!
23//! - `ValidatedJson<T>` (feature `validator`) - deserializes a JSON body and runs
24//!   `validator` validation before the handler runs.
25//! - `Pagination` and `CursorPagination` (feature `extract`) - parse `limit`/`offset` and
26//!   `cursor`/`limit` query parameters into typed values, with `list_response` /
27//!   `cursor_response` helpers that build the matching response type.
28//!
29//! # Quick Start
30//!
31//! ```rust,no_run
32//! use axum::{Json, http::StatusCode, response::IntoResponse};
33//! use axum_api_kit::{ApiError, ListResponse, CursorResponse, HealthResponse};
34//! use serde::Serialize;
35//!
36//! #[derive(Serialize)]
37//! struct Item { id: String }
38//!
39//! async fn list_items() -> impl IntoResponse {
40//!     let items = vec![Item { id: "1".into() }];
41//!     Json(ListResponse { data: items, total: 1, limit: 50, offset: 0 })
42//! }
43//!
44//! async fn feed_items(cursor: Option<String>) -> impl IntoResponse {
45//!     let items = vec![Item { id: "1".into() }];
46//!     CursorResponse { data: items, next_cursor: Some("abc".into()), has_more: true }
47//! }
48//!
49//! async fn get_item() -> impl IntoResponse {
50//!     ApiError::not_found("item not found")
51//! }
52//!
53//! async fn health() -> impl IntoResponse {
54//!     HealthResponse::ok()
55//! }
56//! ```
57
58mod cursor;
59mod error;
60mod health;
61mod list;
62#[cfg(feature = "extract")]
63mod pagination;
64#[cfg(feature = "validator")]
65mod validated;
66
67pub use cursor::CursorResponse;
68pub use error::ApiError;
69pub use health::HealthResponse;
70pub use list::ListResponse;
71#[cfg(feature = "extract")]
72pub use pagination::{CursorPagination, Pagination};
73#[cfg(feature = "validator")]
74pub use validated::ValidatedJson;