Skip to content

haddowg/json-api — a server-side JSON:API 1.1 library for PHP

haddowg/json-api is a modern, server-side JSON:API 1.1 library for PHP 8.3, 8.4 and 8.5. It is framework- and storage-agnostic: you bring a domain model and a PSR-7/PSR-17 implementation, declare your resource types, and the library handles negotiation, body parsing, sparse fieldsets, includes, error rendering, and encoding. You stay in plain PHP value objects throughout — there is no router and no ORM baked in.

What it does

The library exists to do four things well:

  • Verifiable JSON:API 1.1 compliance. The spec's MUST/SHOULD rules are tracked, and the example app's test suite asserts every response is spec-compliant.
  • First-class server-side profiles. Profiles are a built-in extension point, not an afterthought — you register them on the server and they participate in content negotiation.
  • A shipped PSR-15 middleware suite for the standard request lifecycle — content negotiation, body parsing, error handling — so the common path is wired for you. See middleware.
  • A stable, production-suitable foundation. Immutable value objects, a typed exception hierarchy, and a single field declaration that drives both serialization and hydration.

Scope boundaries

This is a server-side library. The following are deliberately out of scope:

  • Client-side consumption — building or parsing requests as an API consumer.
  • Framework integration. The library is framework-agnostic; idiomatic Symfony integration (DI, routing, the request lifecycle) lives in a separate bundle, haddowg/json-api-symfony.
  • Migration tooling — there are no codegen or schema-migration helpers.

Install

composer require haddowg/json-api

The library depends on a PSR-7 HTTP message implementation and a PSR-17 factory; it does not ship one. The examples and tests use nyholm/psr7:

composer require nyholm/psr7

A taste

You declare a resource type by subclassing AbstractResource, giving it a $type, and listing its fields. One field declaration drives both directions — read and write:

use haddowg\JsonApi\Resource\AbstractResource;
use haddowg\JsonApi\Resource\Field\Id;
use haddowg\JsonApi\Resource\Field\Str;

final class AlbumResource extends AbstractResource
{
    public static string $type = 'albums';

    public function fields(): array
    {
        return [
            Id::make(),
            Str::make('title')->required()->maxLength(200)->sortable(),
            // …
        ];
    }
}

You then register the resource on an immutable Server — the per-version configuration root — and wire your PSR-17 factories:

use haddowg\JsonApi\Server\Server;
use Nyholm\Psr7\Factory\Psr17Factory;

$psr17 = new Psr17Factory();

$server = Server::make()
    ->withBaseUri('https://music.example')
    ->withPsr17($psr17, $psr17)
    ->register(AlbumResource::class);

This is a sketch, not a copy-paste runnable program — a real service also wires a middleware chain, a router, and an operation handler. The getting-started walkthrough builds the whole thing end to end against the music-catalog example app, with every snippet lifted from a CI-run test.

Design philosophy

A few principles run through the whole library:

  • Immutable value objects. The Server, responses, and parsed request data are readonly; every with…() method returns a new instance.
  • Enums and typed exceptions. Fixed vocabularies (relationship modes, comparisons, media-type parameters) are enums; failures are a typed exception hierarchy carrying their own HTTP status.
  • One declaration, both directions. A field listed in fields() describes how a member serializes and how it hydrates — you do not maintain two parallel maps.
  • First-class profiles. Profiles are a registered server concern, negotiated like any other media-type parameter.

Credits and licence

This package derives substantial portions from woohoolabs/yin; its original authors' copyright is preserved alongside this derivative work. The fluent field and constraint layer is inspired by Laravel JSON:API. It is released under the MIT licence with a dual copyright (Gregory Haddow; Woohoo Labs and contributors) — see LICENSE.

Where to go next

Start with the getting-started walkthrough, then reach for the reference pages as you need them: