jsonapi_http 1.0.0-rc.1

Framework-agnostic HTTP integration layer for jsonapi_core: tower services, request parsing, and response building over the `http` crate types
Documentation

jsonapi_http

Framework-agnostic JSON:API v1.1 HTTP integration for jsonapi_core — request parsing, response building, and tower middleware over the http crate's types, with no web-framework dependency.

If you are building an axum service, use jsonapi_axum instead — it provides the FromRequest / IntoResponse glue on top of this crate. Depend on jsonapi_http directly only when writing an adapter for another framework (actix, warp, …): the same building blocks back every adapter.

What you get

Plain functions and tower layers over http::Request / http::Response:

Module Item Role
request check_content_type Validate the request Content-Type is application/vnd.api+json (→ 415 / 400).
request negotiate Negotiate a response media type from Accept, intersecting requested ext/profile with the server's (→ 406).
request parse_query Parse the URI query into a typed jsonapi_core::Query.
request deserialize_body Deserialize collected body Bytes into a typed Document<T>, mapping failures to an ApiError with source.pointer.
response document_response / json_api_response Serialize a Document into a JSON:API http::Response (200, or any status).
response content_type_value Build a Content-Type header value carrying negotiated ext/profile.
error status_for / to_api_error Map a jsonapi_core::Error to an HTTP status and a JSON:API ApiError.
error error_response / error_response_for / error_response_for_status Build a spec-shaped JSON:API error-document response (from ApiErrors, an Error, or a bare status).
error with_status + ApiErrorExt / ApiErrors Fluently build an ApiError from a status and accumulate several into one document.
id ClientIdPolicy / check_client_id / check_id_matches / id_conflict Enforce client-supplied id policy on create (Assign/Accept/Forbid) and PATCH id-matching.
include IncludeResolver / resolve_includes Assemble a deduped compound-document included array from include paths via a batch loader (one load per type per level — no N+1).
layer ContentTypeLayer / AcceptLayer / JsonApiLayer tower layers that reject non-conforming requests (415 / 406) before they reach a handler.

Every failure — from an extractor, a layer, or a handler — funnels through error_response, so the wire format of an error is identical no matter where it was raised.

Install

[dependencies]
jsonapi_http = "1.0.0-rc.1"
jsonapi_core = "1.0.0-rc.1"

Building an adapter

An adapter reads the relevant part of a request, calls into jsonapi_http, and turns any Error into an error response. For example, a body extractor:

use jsonapi_http::{check_content_type, deserialize_body, error_response_for};

fn extract_document<T>(req: &http::Request<bytes::Bytes>) -> http::Response<bytes::Bytes>
where
    T: jsonapi_core::ResourceObject + serde::de::DeserializeOwned,
{
    if let Err(err) = check_content_type(req.headers()) {
        return error_response_for(&err);
    }
    match deserialize_body::<T>(req.body()) {
        Ok(_document) => todo!("hand the typed document to the handler"),
        Err(err) => error_response(std::iter::once(*err)),
    }
}

The tower layers drop into any tower-based router unchanged:

use jsonapi_http::JsonApiLayer;
// service.layer(JsonApiLayer::new().ext(["https://jsonapi.org/ext/atomic"]))

Scope

This crate parses and represents sort / filter / page; applying them to a datastore is the consumer's job (a deliberate non-goal). It never buffers the request body — the adapter drains the body into Bytes and enforces any body-size limit before calling deserialize_body.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.