tobscure/json-api-server
A PHP library for building JSON API-compliant APIs, providing serializers, resource transformers, and relationship handling. Framework-agnostic and easy to integrate into Laravel or any PHP project.
Architecture fit
This package is the JSON:API layer for a Laravel application: it sits between the HTTP kernel and Eloquent, owning content negotiation, request validation, serialization, error documents, and OpenAPI schema generation. v1.0.0-rc.1 is not an incremental release — it is a structural refactor of three subsystems (endpoints, query parameter validation, schema generation) plus a rework of the resource ID contract. For a TPM this matters because the package is not a peripheral utility: it defines the external API contract, so its breaking changes are effectively breaking changes to our public API.
Integration feasibility
Feasible, but only with a scheduled migration rather than an opportunistic bump. The breaking changes split into three tiers of effort:
json_api_response(), Context::queryParam(), Extension\Atomic, Pagination\Concerns\BuildsUrls), the SingleError → JsonApiError trait rename, and the getJsonApiErrors() → getJsonApiError() signature flip. These surface as hard fatals at boot or dispatch, which is good: they fail loudly and early.Resource::getId() → Resource::id(): Id. This moves ID definition from an imperative method into a declarative field object that also carries type constraints, validation rules, and write permissions. Any resource with custom ID logic needs re-modelling, not just renaming.ToOne custom deserializers now receive the resolved model instead of the raw relationship object. This will not raise an error; it will produce wrong data if the deserializer was written against the old contract. This is the highest-risk item in the release.Technical risk
JsonApiErrorsException. Clients that assumed a stable array shape may break on parsing even though the JSON:API spec is still honoured.Id::writableOnCreate() allows client-generated IDs. That is a deliberate capability, but it needs a policy decision per resource, not a default.Key questions
getId() → id(): Id migration?Stack fit
The package is idiomatic Laravel and composes cleanly with the surrounding stack: routing and middleware are unchanged, authorization continues through Laravel policies and gates, and authentication (Sanctum/Passport) is orthogonal to the upgrade. The new Endpoint::headers(), Endpoint::response(), and endpoint-hook surface actually improves fit for teams that previously had to reach around the package to customise responses. The asynchronous processing support (Create::async() → 202, Show::seeOther() → 303) is a genuine architectural fit for queue-backed writes and fits naturally with Laravel's queue primitives.
Migration path
Compatibility
Accept parsing and Content-Type emission. Cursor pagination now auto-activates the cursor-pagination profile — this is a response-header change that existing clients may not expect.Show/Update into aggregate *Resource and *Relationship endpoints. Route names and generated schema paths may shift; check any hardcoded client routes.Sequencing
JsonApiError trait, helper removals, namespace moves. Low ambiguity, high error visibility, unblocks the rest.Resource::id(): Id conversion, with an explicit per-resource decision on type constraints and writability. Highest effort and highest data-integrity exposure.Type schema support — each as an independently justified piece of scope, not bundled into the upgrade.Maintenance
Expect continued churn while the package is in RC. Budget recurring maintenance rather than a one-time upgrade cost: pin the version explicitly, track release notes as a standing agenda item, and avoid floating constraints. Consider a thin internal adapter layer around the highest-churn surfaces (error providers, ID resolution) to shrink the blast radius of the next breaking release.
Support
This is community OSS with a single-maintainer risk profile. There is no vendor SLA. The mitigation is internal: at least two engineers who understand the package internals, a documented upgrade runbook, and a decision record explaining any place we diverge from upstream conventions. Without that, the next breaking release becomes a heroic single-person effort.
Scaling
The release is net-positive here. Serialization performance optimisations directly reduce per-request cost on the hottest path in a JSON:API service. Cursor pagination range truncation (Page::$rangeTruncated) matters for large or unbounded result sets, where truncated ranges were previously ambiguous to clients. Asynchronous processing lets long-running create operations leave the request cycle, which improves tail latency and reduces timeout-driven retries. Resource and relationship links, plus richer Type support, reduce ad-hoc custom serialization that typically accumulates as a service grows.
Failure modes
ToOne deserializers receiving a resolved model. Highest severity, lowest detectability. Mitigate with explicit integration tests per deserializer.Content-Type.Ramp-up
The conceptual load is unevenly distributed. The error provider and trait changes are an afternoon. The Id field, SchemaContext, the new Type hierarchy (Obj, AnyOf, AllOf, OneOf, Not, nullable()), and endpoint hooks represent a genuine learning curve — the mental model moves from imperative overrides to declarative field definitions. Plan for a spike by one engineer, a written internal pattern document, and a staged rollout through a non-critical API surface before touching customer-facing endpoints. Allocate meaningful time to the OpenAPI re-baseline; it is consistently underestimated and is the step most likely to surface unexpected client impact.
Recommendation. Treat rc.1 as a planned migration, not a routine dependency bump. Gate the work on an inventory, a captured behavioural baseline, and a clear answer on GA timing; sequence the breaking changes ahead of feature adoption; and require explicit sign-off on the ID writability decision and the error-shape change before anything reaches production.
How can I help you explore Laravel packages today?