jsonapi_core 1.0.0-rc.1

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

[![Crates.io](https://img.shields.io/crates/v/jsonapi_core.svg)](https://crates.io/crates/jsonapi_core)
[![Documentation](https://docs.rs/jsonapi_core/badge.svg)](https://docs.rs/jsonapi_core)
[![CI](https://github.com/rankitbishnoi/jsonapi_core/actions/workflows/ci.yml/badge.svg)](https://github.com/rankitbishnoi/jsonapi_core/actions/workflows/ci.yml)
[![MSRV](https://img.shields.io/badge/MSRV-1.94.1-blue.svg)](https://github.com/rankitbishnoi/jsonapi_core)
[![License](https://img.shields.io/crates/l/jsonapi_core.svg)](#license)

A typed [JSON:API v1.1](https://jsonapi.org/format/) 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 `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 negotiation**`ext`/`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

```sh
cargo add jsonapi_core
```

Requires Rust **1.94.1+** and the **2024 edition**.

## Quick Example

```rust
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_axum`]https://docs.rs/jsonapi_axum**[axum]https://docs.rs/axum
  extractors (`JsonApi<T>`, `JsonApiQuery`), responders (`JsonApiResponse`), and
  content-negotiation middleware (`JsonApiLayer`). Start here for an axum service.
- **[`jsonapi_http`]https://docs.rs/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`](https://github.com/rankitbishnoi/jsonapi_core/blob/main/docs/feature-flags.md) for details.

## Examples

Runnable examples ship with the crate:

```sh
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]https://github.com/rankitbishnoi/jsonapi_core/blob/main/docs/SUMMARY.md** — 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]https://docs.rs/jsonapi_core** — type-level reference
  for every public item.

The guide is laid out as an [mdbook](https://rust-lang.github.io/mdBook/) under
`docs/`. To build it locally:

```sh
cargo install mdbook
mdbook serve docs
```

Or browse the markdown directly starting at [`docs/introduction.md`](https://github.com/rankitbishnoi/jsonapi_core/blob/main/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]https://docs.rs/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](https://semver.org/spec/v2.0.0.html).

### 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]https://github.com/rankitbishnoi/jsonapi_core/blob/main/docs/SUMMARY.md: the fuzzy-deserialization alias set, the
  `Option::None` → omitted-on-serialize rule, the `null``None` 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](https://github.com/rankitbishnoi/jsonapi_core/blob/main/CHANGELOG.md).

### 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`](https://github.com/rankitbishnoi/jsonapi_core/blob/main/CHANGELOG.md) for a release-by-release record.

## License

Licensed under either of [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
or [MIT license](http://opensource.org/licenses/MIT) at your option.