laravel-json-api/non-eloquent
Laravel JSON:API adapter for non-Eloquent data sources, letting you expose custom repositories or external APIs through the JSON:API spec with resources, relationships, filtering, sorting, and pagination—no Eloquent models required.
Architecture fit. This is an extension, not a standalone library — it plugs into an existing laravel-json-api/json-api installation and adds an adapter path for non-Eloquent data. Architecturally it is a thin adapter: the abstract Schema class doubles as schema and storage contract, so the integration surface is a small set of method signatures (all(), find(), optionally create()/update()/delete()) plus field resolvers. That shape is favorable — it keeps persistence in a repository/service layer and leaves the API layer as a declarative shell. It fits cleanly when JSON:API is already the house standard and a minority of resources have no table behind them (aggregations, reporting views, cached DTOs, integration payloads, feature flags). It is a poor fit if most resources would be non-Eloquent — that is a sign the architecture is wrong, not something to patch.
Integration feasibility. Low mechanical cost. composer require, extend the base schema, register in Server::allResources() alongside Eloquent schemas, implement only the storage methods you need, and routes work as usual (JsonApiRoute::server('v1')->resources()->all()). Filters, sorts, pagination, sparse fieldsets, includes, and query-parameter validation are inherited from the parent — the entire point of the package is conformance, not novelty. The prerequisite is non-negotiable: a working parent install, a registered Server, and at least one green Eloquent-backed resource before you start, otherwise you cannot distinguish a broken adapter from a broken baseline. A time-boxed afternoon spike is realistic.
Technical risk. The dominant risk is maturity, not code. Four stars and zero known dependents means you are the compatibility test suite. Specific hazards:
laravel-json-api/* major; PHP, Laravel, and parent constraints must all line up. A mismatch is a fork decision, not a workaround.find(), and includes in ways that surface far from the cause.include over a remote source fans out into a call per parent unless the repository preloads in bulk.Key questions to answer before committing:
all()/find() only).authorize(), or a scoping hook?Stack fit. Laravel + laravel-json-api in production, JSON:API as the documented contract, and an API surface that is predominantly Eloquent-backed. Where those hold, the fit is clean because everything above the storage layer is unchanged — the day-to-day configuration you already do (filters, sorts, pagination, request validation) carries over verbatim.
Migration path. Incremental, resource-by-resource. The package directly replaces the fake-model anti-pattern (wrapping an external call in a stub Eloquent model, which creates invisible coupling and query-builder paths that never execute) and it decouples reporting, dashboard, and integration endpoints from schema/database workstreams — a delivery-date decision as much as a technical one. It also supports moving a legacy surface to JSON:API without a big-bang rewrite gated on data-layer work.
Compatibility. Read the vendored composer.json and pin both packages explicitly. The vendored Schema.php abstract method signatures are the contract — they override anything found online. Treat the package as a small vendored utility: budget an afternoon to read it fully and be able to fork it. It is a few hundred lines of thin adapter.
Sequencing.
Server registered, one Eloquent resource returning a correct JSON:API document.Schema, the Store contract, the README version matrix.Server or route group so you can A/B the response shape without disturbing Eloquent resources.all() and find(). This exercises routing, serialization, includes, fieldsets, and pagination without touching writes.JsonApiRequest classes. Business rules stay out of schemas.type, id, attributes, relationships, links, meta) are green.Design rule to enforce in review. Wrap a repository, not an array. Keep persistence and fetch logic in a service the app already uses, so the backing source (static array → Redis → API) can change without touching the API layer — and so each schema can be reimplemented as a custom adapter if the package stalls.
Maintenance. Low code volume, low churn, but you are the maintenance plan. Pin versions, assign a named owner watching upstream, and budget a small internal fork or wrapper as a plausible outcome rather than a disaster. Keep schemas thin and repositories thick so the exit path stays cheap: if the package stalls, each schema becomes a custom adapter over the same repository and day-to-day code does not move.
Support. No vendor SLA, no community body of answers, no dependents to learn from. The safety net is contract tests plus an internal owner. Do not make it a critical-path dependency without that owner named.
Scaling. Treat the upstream as slow by default. Batch aggressively at the repository layer: preload related records in bulk for includes, cache the result of all(), and assume every include is a fan-out unless proven otherwise. Do not put expensive computation in resolvers — sparse fieldsets mean they may never run, and when they do run it is per-record.
Failure modes to design and test for:
find(), and includes.Ramp-up. The adapter surface is tiny; most bugs live in your resolvers and your IDs, so give the team an explicit debugging order: confirm the schema is registered in allResources(), confirm find() returns the expected object, confirm each resolver returns the expected type, inspect the raw response for id/type linkage, and only then look at the parent package's serialization. That sequence, plus the "read the vendored source first" habit, is the whole onboarding curve — roughly a day for a competent Laravel developer, plus the spike time on relationships and external pagination.
How can I help you explore Laravel packages today?