enm/json-api-common
JSON API Common provides shared PHP classes and interfaces for building JSON:API-compliant servers and clients. It powers enm/json-api-server and enm/json-api-client, covering resources, relationships, links, documents, errors, and requests.
enm/json-api-common is not a JSON:API serializer you install and point at your models. It is the shared vocabulary of classes and interfaces (documents, resources, links, errors, requests/responses) that enm/json-api-server and enm/json-api-client both depend on. Most days you will pull it in transitively; you only add it directly when you need to build or inspect JSON:API structures yourself (custom serializers, middleware, test fixtures, or a bespoke server layer).
Minimum steps:
composer require enm/json-api-common
Then read the docs in the order the README lists them — that ordering is deliberately bottom-up:
docs/01-trait.md — JsonApiTrait, the type/id identity contract every resource shares.docs/02-resources.md — ResourceObject vs ResourceIdentifier, and relationships.docs/03-collections.md — attributes and meta on collections.docs/04-links.md — link objects versus plain strings.docs/05-documents.md — the Document container (data, included, meta, links, errors, jsonapi).docs/06-errors.md / docs/07-requests.md — error objects and request/response wrappers.First use case: build a minimal document by hand and inspect it, so you understand the object graph before letting the server package generate it for you.
use enm\JsonApi\Model\Document\Document;
use enm\JsonApi\Model\Resource\Resource;
$resource = new Resource('articles', '1');
$resource->attributes()->set('title', 'Hello JSON:API');
$document = new Document($resource);
$document->links()->set('self', '/articles/1');
echo json_encode($document); // {"data":{...},"links":{...}}
If that round-trips into the shape you expect, you have the mental model needed for everything else. If you just want to serve an API, stop here and install enm/json-api-server instead — it is the package that actually does the work.
Treat the package as shared value objects + interfaces. The two common patterns are (a) constructing documents for output, and (b) implementing the provided interfaces in your own code so that server/client packages accept your classes.
Document-centric assembly for custom output. Rather than hand-rolling JSON:API arrays, assemble a Document tree and json_encode it. This matters when you write a custom responder, an export job, or a webhook payload — you get spec compliance (data/included/links/meta/errors) for free and a single place to fix mistakes.
Relationships between resources. Use ResourceIdentifier when you only need linkage (type + id) and ResourceObject when the resource carries attributes. Then attach relationships with a Relationship object and control whether related data is in included via the document. This is the classic trade-off: full linkage in included vs. sparse identifiers plus a related link.
Identifier-only responses. For POST/PATCH that returns 204-adjacent payloads or link-only responses, ResourceIdentifier is the right type — it keeps you from accidentally bloating responses with attributes you don't want.
Error documents instead of ad-hoc arrays. Build Error objects with status, code, title, detail, source (pointer/parameter) and wrap them in a Document with errors set. This is the most valuable reuse in the package: consistent RFC-style error payloads across controllers and middleware.
Mirroring server behaviour. When you need to override a transformer or write a test double, implement the same interfaces your server uses. Because both server and client depend on this package, the types line up exactly — that is the whole point of its existence.
Serialization boundary. Keep your domain models out of this package's classes: transform at the edge (controller/responder), so a change in your persistence layer doesn't ripple into document construction.
tobyzernitz/…, woohoolabs/json-api, or swagger-style alternatives exist, or just hand-build arrays.jsonapi($model) to work, you want json-api-server/json-api-client. Almost every "this package does nothing" complaint stems from this.Resource vs ResourceIdentifier types. PHP will happily accept one where the other is expected in some places and produce missing attributes elsewhere. Be explicit in method signatures.null data, an absent key, and [] mean different things. Setting an empty attribute collection is not the same as omitting it — check what your document actually emits, not what you intended.self string versus Link with meta and type are both legal; mixing conventions across endpoints confuses clients. Pick one and enforce it in a helper.included is your responsibility. The document does not deduplicate or validate compound documents. Duplicate identifiers in included will produce invalid JSON:API; key included resources by type:id in a map before attaching.meta vs attributes. Metadata belongs on the resource's meta object, not in attributes. Moving it later is a breaking API change for clients.var_dump(json_encode($document, JSON_PRETTY_PRINT)) is the fastest diagnostic; inspect data, included, and relationships keys in that order. When the server package produces odd output, reproduce it with the common classes directly to prove whether the bug is in serialization or in your resource mapping.How can I help you explore Laravel packages today?