jsonapi_core 1.0.0-rc.1

A typed JSON:API v1.1 serialization library for Rust
Documentation

jsonapi_core

Crates.io Documentation CI MSRV License

A typed JSON:API v1.1 serialization library for Rust.

Features

  • Full type modelDocument, Resource, Relationship, Link, ApiError, and all JSON:API 1.1 types with custom serde implementations
  • Derive macro#[derive(JsonApi)] maps Rust structs to JSON:API resource envelopes
  • Fuzzy deserialization — accepts camelCase, snake_case, kebab-case, and PascalCase variants of field names
  • Registry — typed lookups from included arrays via Relationship<T> references
  • Recursive resolver — kitsu-core-style flattened output with cycle detection
  • Query builder — JSON:API-aware query strings with bracket encoding and RFC 3986 percent-encoding
  • Query parsing — server-side parsing of sort/include/fields/page/filter into a typed Query
  • Cursor pagination — the JSON:API cursor-pagination profile (CursorPage, CursorLinks)
  • Response builder — fluent DocumentBuilder for assembling compound documents, plus error/meta-only constructors
  • Content negotiationext/profile media-type parsing, Content-Type validation, Accept negotiation
  • Sparse fieldsets — typed and dynamic filtering paths
  • Include path validation — relationship graph walking with static type metadata
  • Atomic Operations extension — feature-gated atomic module implementing the JSON:API v1.1 Atomic Operations extension (add/update/remove, lid cross-refs)

Install

cargo add jsonapi_core

Requires Rust 1.94.1+ and the 2024 edition.

Quick Example

use jsonapi_core::{Document, JsonApi, Relationship, Resource};

#[derive(Debug, Clone, PartialEq, JsonApi)]
#[jsonapi(type = "articles")]
struct Article {
    #[jsonapi(id)]
    id: String,
    title: String,
    #[jsonapi(relationship, type = "people")]
    author: Relationship<Person>,
}

#[derive(Debug, Clone, PartialEq, JsonApi)]
#[jsonapi(type = "people")]
struct Person {
    #[jsonapi(id)]
    id: String,
    name: String,
}

// Deserialize a JSON:API response
let json = r#"{
    "data": {
        "type": "articles", "id": "1",
        "attributes": {"title": "Hello JSON:API"},
        "relationships": {
            "author": {"data": {"type": "people", "id": "9"}}
        }
    },
    "included": [{
        "type": "people", "id": "9",
        "attributes": {"name": "Dan Gebhardt"}
    }]
}"#;

// Use Document<Resource> to handle mixed types in `included`
let doc: Document<Resource> = serde_json::from_str(json).unwrap();
let registry = doc.registry().unwrap();

// Typed lookup — deserializes the stored Value into a Person
let author: Person = registry.get_by_id("people", "9").unwrap();
assert_eq!(author.name, "Dan Gebhardt");

Building an HTTP server

jsonapi_core is transport-agnostic. Two companion crates turn it into a server stack:

  • jsonapi_axumaxum extractors (JsonApi<T>, JsonApiQuery), responders (JsonApiResponse), and content-negotiation middleware (JsonApiLayer). Start here for an axum service.
  • jsonapi_http — the framework-agnostic layer the adapters are built on (request parsing, response building, tower layers). Depend on it directly to write an adapter for another framework.

Feature Flags

Feature Default Description
derive yes Re-exports #[derive(JsonApi)] from jsonapi_core_derive
atomic-ops off Atomic Operations extension types (atomic module)

See docs/feature-flags.md for details.

Examples

Runnable examples ship with the crate:

cargo run --example basic_serialize       -p jsonapi_core
cargo run --example basic_deserialize     -p jsonapi_core
cargo run --example dynamic_resource      -p jsonapi_core
cargo run --example query_builder         -p jsonapi_core
cargo run --example content_negotiation   -p jsonapi_core
cargo run --example atomic_operations     -p jsonapi_core --features atomic-ops

Documentation

  • The jsonapi_core Guide — chapter-by-chapter walkthrough covering documents, resources, relationships, the registry, the query builder, query parsing, sparse fieldsets, cursor pagination, building responses, content negotiation, atomic operations, and a cookbook of common recipes.
  • API docs on docs.rs — type-level reference for every public item.

The guide is laid out as an mdbook under docs/. To build it locally:

cargo install mdbook
mdbook serve docs

Or browse the markdown directly starting at docs/introduction.md.

Repository layout

Path Contents
jsonapi_core/ The library crate.
jsonapi_core_derive/ The proc-macro crate (re-exported via the derive feature).
jsonapi_core_validation/ Shared member-name validation used by both the runtime and derive crates (internal implementation detail).
jsonapi_http/ Framework-agnostic HTTP integration (request parsing, response building, tower layers).
jsonapi_axum/ axum adapter: JSON:API extractors, responders, and middleware.
acceptance/ Spec-conformance integration tests.
docs/ The guide book (this is what you're reading).

Versioning policy

jsonapi_core follows Semantic Versioning 2.0.0.

Lockstep workspace versions

All five publishable crates — jsonapi_core, jsonapi_core_derive, jsonapi_core_validation, jsonapi_http, and jsonapi_axum — are versioned in lockstep via workspace.package.version and released together under one tag. Pin only the crates you depend on directly (e.g. jsonapi_core for the type model, jsonapi_axum for an axum service); jsonapi_core_derive is re-exported via the derive feature and jsonapi_core_validation is an internal detail.

What is public API

The following are public API and changes to them are governed by SemVer:

  • All items re-exported at the jsonapi_core crate root (Document, PrimaryData, Resource, ResourceObject, ResourceIdentifier, ResourceRelationship, ResourceType, Identity, Relationship, RelationshipData, Links, Link, LinkObject, Hreflang, Meta, HasLinks, HasMeta, Field, JsonApiObject, ApiError, ErrorLinks, ErrorSource, Registry, ResolveConfig, TypeRegistry, TypeInfo, QueryBuilder, Query, SortField, FieldsetConfig, SparseSerializer, sparse_filter, DocumentBuilder, CursorPage, CursorLinks, CURSOR_PAGINATION_PROFILE, OffsetPage, PageNumberPage, PageStrategy, PageWindow, PaginationLinks, CaseConfig, CaseConvention, Error, Result, Cardinality, JsonApiMediaType, validate_content_type, negotiate_accept, validate_member_name, MemberNameKind).
  • All items re-exported under the atomic-ops feature (AtomicRequest, AtomicResponse, AtomicResult, AtomicOperation, OperationTarget, OperationRef, ATOMIC_EXT_URI).
  • The #[derive(JsonApi)] attribute set: type, case on the struct; id, lid, relationship, meta, links, rename, skip, and relationship type on fields.
  • Default behaviours documented in the crate-level rustdoc and the guide: the fuzzy-deserialization alias set, the Option::None → omitted-on-serialize rule, the nullNone deserialize fall-through, the registry's silent skip on shape mismatch, the resolver's cycle detection.
  • The minimum supported Rust version (MSRV).

What is not public API

  • Anything inside a pub(crate) or private module path. Items not re-exported at the crate root may be relocated or removed in any release.
  • The exact text of error Display messages (the Error enum variants are public; the formatted strings are not).
  • The exact alias ordering inside the fuzzy-deserialization fall-through chain (the set of accepted aliases is public; ties resolve in implementation order).
  • Internals of the jsonapi_core_derive proc-macro crate. Use the derive only through jsonapi_core's derive feature; do not depend on jsonapi_core_derive directly.
  • Unreleased items behind unstable feature flags (none currently exist; this applies to any future unstable_* flags).

#[non_exhaustive] guarantees

All public enums (PrimaryData, Document, RelationshipData, Identity, Hreflang, Link, CaseConvention, MemberNameKind, Error, …) carry #[non_exhaustive]. New variants may be added in minor releases. Match arms in consumer code must include a _ => fall-through.

MSRV policy

The minimum supported Rust version is currently 1.94.1. MSRV bumps require a minor-version release (≥ 0.x.0 while pre-1.0; ≥ x.0.0 post-1.0) and will be called out in the changelog.

Stability

The public API described above is stable as of the 1.0 line. Breaking changes to it require a major-version bump (2.0.0); new functionality ships in minor releases and fixes in patch releases, per SemVer. A detailed changelog accompanies every release so each upgrade has a clear migration path.

1.0.0-rc.1 is the first release candidate: the API is frozen for the RC period so consumers can validate it before the final 1.0.0 tag. Report any issue you would want addressed before 1.0.0 via the tracker.

Changelog

See CHANGELOG.md for a release-by-release record.

License

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