jsonapi_core
A typed JSON:API v1.1 serialization library for Rust.
Features
- Full type model —
Document,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
includedarrays viaRelationship<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/filterinto a typedQuery - Cursor pagination — the JSON:API cursor-pagination profile (
CursorPage,CursorLinks) - Response builder — fluent
DocumentBuilderfor assembling compound documents, plus error/meta-only constructors - Content negotiation —
ext/profilemedia-type parsing,Content-Typevalidation,Acceptnegotiation - Sparse fieldsets — typed and dynamic filtering paths
- Include path validation — relationship graph walking with static type metadata
- Atomic Operations extension — feature-gated
atomicmodule implementing the JSON:API v1.1 Atomic Operations extension (add/update/remove, lid cross-refs)
Install
Requires Rust 1.94.1+ and the 2024 edition.
Quick Example
use ;
// 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: = from_str.unwrap;
let registry = doc.registry.unwrap;
// Typed lookup — deserializes the stored Value into a Person
let author: Person = registry.get_by_id.unwrap;
assert_eq!;
Building an HTTP server
jsonapi_core is transport-agnostic. Two companion crates turn it into a server stack:
jsonapi_axum— axum 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:
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:
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_corecrate 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-opsfeature (AtomicRequest,AtomicResponse,AtomicResult,AtomicOperation,OperationTarget,OperationRef,ATOMIC_EXT_URI). - The
#[derive(JsonApi)]attribute set:type,caseon the struct;id,lid,relationship,meta,links,rename,skip, and relationshiptypeon fields. - Default behaviours documented in the crate-level rustdoc and the
guide: the fuzzy-deserialization alias set, the
Option::None→ omitted-on-serialize rule, thenull→Nonedeserialize 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
Displaymessages (theErrorenum 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_deriveproc-macro crate. Use the derive only throughjsonapi_core'sderivefeature; do not depend onjsonapi_core_derivedirectly. - 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.