Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Json Api Bundle Laravel Package

rikudou/json-api-bundle

Symfony bundle that brings JSON:API support to PHP applications. It handles request parsing, resource serialization, relationships, filtering, pagination, and standardized error responses, helping teams build consistent, spec-friendly APIs with less boilerplate and fewer custom endpoints.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package — there is no service provider, no package auto-discovery, and no config/json-api.php. Before writing any code, decide which of two paths you're on:

  1. You're actually on Symfony (or willing to run a Symfony-kernel app alongside Laravel). Register the bundle in config/bundles.php, confirm the rikudou_json_api config key loads, and proceed normally.
  2. You're on Laravel. Do not install the bundle and expect it to wire itself. Instead, either (a) pull in the underlying framework-agnostic rikudou/json-api library and write a small service provider, or (b) read the bundle's src/Resources/config/services.yaml, copy the handful of service definitions into a Laravel provider, and translate Symfony constructor args to container bindings. Budget an afternoon for either. If the API surface is going to be large, weigh laravel-json-api / cloudcreativity/laravel-json-api instead — that is the mainstream Laravel-native choice and will save you the porting work.

Minimal install:

composer require rikudou/json-api-bundle

Then read, in this order:

  1. src/Resources/config/services.yaml — the authoritative map of service IDs and constructor arguments. The README is thin; trust the source over any blog post.
  2. The Attribute/ directory — resource/type, id, attribute, and relationship markers live here. Names shift between versions, so verify against the installed tag.
  3. The rikudou/json-api dependency — ObjectManager, Serializer, Document, ResourceObject. The bundle is mostly glue; the actual document behavior lives there.
  4. tests/ — the fastest way to see a complete document assembled end to end.

First use case: one resource, one endpoint

Annotate a single class, serialize it, return it. Keep the first slice embarrassingly small so you can validate spec conformance before building on it.

use Rikudou\JsonApiBundle\Attribute\ApiResource;
use Rikudou\JsonApiBundle\Attribute\ApiId;
use Rikudou\JsonApiBundle\Attribute\ApiAttribute;

#[ApiResource(type: 'articles')]
class Article
{
    #[ApiId]
    public int $id;

    #[ApiAttribute]
    public string $title;
}
$document = $this->serializer->serialize($article);

return response()->json($document, 200, [
    'Content-Type' => 'application/vnd.api+json',
]);

Verify with curl before writing anything else:

  • Content-Type is application/vnd.api+json (not application/json).
  • data.type equals articles; data.id is a string.
  • data.attributes.title is present and not nested under data.attributes.data.
  • A links.self entry exists if you intend to advertise it.

If any of those are wrong, stop and fix it. Every downstream client assumption depends on this base shape.

Implementation Patterns

Attribute-driven resources, thin controllers. Serialization belongs at the boundary, not in controllers. Inject the serializer, hand it a model (or a dedicated DTO), return the document. Keep one resource class per exposed type and don't expose Eloquent/Doctrine entities directly if the API shape may diverge from the schema — a DTO layer costs little now and saves a rewrite later.

Porting the container to Laravel. If you took path 2 above, the provider is small and the pattern is mechanical:

// app/JsonApi/JsonApiServiceProvider.php
public function register(): void
{
    $this->app->singleton(ObjectManager::class, fn () => new /* ... */);
    $this->app->singleton(Serializer::class, fn ($app) => new /* ... */);
}

Fill the constructor arguments by reading services.yaml line by line. Anything that takes a Symfony-specific dependency (event dispatcher, HTTP message factory, router) must be swapped for the Laravel equivalent or stubbed. This is the highest-friction part of the port; do it once, in one provider, and keep it isolated.

Relationships and include. Model to-one and to-many relationships as relationships with links and data linkage, and resolve include query parameters at the document-building layer — not by eager-loading everything. The pattern to aim for:

// controller / action
$articles = $this->articleRepository->all();
$document = $this->serializer->serialize($articles, [
    'include' => $request->query('include'),
    'fields'  => $request->query('fields'),
]);

The moment you wire include, instrument your query count. This is where JSON:API implementations become expensive: a naive include=author,comments.user on a 50-item collection is a textbook N+1. Compare query counts with and without includes before you ship.

Sparse fieldsets. Honour fields[articles]=title,author by filtering attributes at serialization time, not by post-processing the array. If the package's attribute layer doesn't do it, wrap the serializer and strip keys in one place so it stays consistent across endpoints.

Standardized error objects. Don't leak exception messages. Map domain exceptions to JSON:API error objects (status, code, title, detail, source.pointer) with a single handler. This is one of the concrete wins of the standard — do it once and every endpoint inherits it.

Custom resource types / extensibility. The package advertises extensible serialization for custom resource types. Use this for computed attributes and synthetic relationships rather than bolting fields onto models. Keep custom serializers in app/JsonApi/Serializers/ so a future framework move is a directory, not an archaeology project.

Conformance test suite. Treat spec conformance as a test target, not a hope. For each resource, assert: document structure, type/id typing, relationship linkage, sparse fieldsets reducing attributes, include producing included, error shape on 404/422, and pagination links. A handful of these catches every upgrade regression you'll hit.

Adoption sequencing. Feature-flag the JSON:API surface, expose a single bounded context first, and keep the old endpoints alive until a real consumer has migrated. Do not migrate an existing REST API onto a zero-adoption bundle in one go.

Gotchas and Tips

Framework mismatch is the first gotcha, not a footnote. The bundle assumes Symfony's bundle/kernel model. Every hour you spend making it fit Laravel is an hour you are not spending on your API — and the maintenance cost recurs at every framework upgrade. Confirm stack fit before the second afternoon.

Media type and Content-Type. JSON:API mandates application/vnd.api+json. Laravel's response()->json() defaults to application/json. Clients that strictly validate the media type (correctly) will reject you. Set the header explicitly, and strip ?include=/?fields= from links.self if you want clean cache keys.

IDs are strings. data.id must serialize as a string per spec, even if your primary key is an integer. A client doing type comparison will break on 1 vs "1". Verify this in the first curl.

N+1 on includes is the classic failure mode. There is no adoption evidence and likely no benchmarks, so you must load-test yourself with realistic compound documents. Log query counts in dev for every endpoint that accepts include.

Version lockout. Zero dependents, 0 stars, single maintainer: assume you will be the maintainers of any patch. Pin to an exact commit or tag, vendor if practical, and check composer.json PHP/Symfony constraints against your exact minor versions during the spike — not after the first upgrade. Verify a test suite exists and runs; absence of popularity is not evidence of quality either way, but absence of CI is a real signal.

Security posture is untested, not proven safe. No CVE history reflects no scrutiny. Treat any user-controlled input that reaches the serializer (include paths, fields keys, filter names) as an injection surface — allow-list them.

Docs are thin, community is absent. Expect no Stack Overflow corpus. Onboarding the next engineer means your own docs. Write the "we own this now" note down before broadening adoption: who patches PHP/Symfony upgrades, what the rollback plan is, and where the fork lives.

Debugging approach that actually works. Serialize one object, dd() the document array, and compare it field-by-field against the spec's example document. Do not debug through the full stack. Most issues are attribute discovery (wrong attribute class, wrong namespace, missing on the right property), relationship linkage, or the media type header.

Extension points to prefer. Custom resource types and a wrapped serializer are safer extension points than patching vendor code. If you must patch, fork and pin — don't rely on an upstream fix.

When to stop. If your API is small, single-consumer, and shallow on relationships, the compound-document and linkage machinery is pure overhead. A thin serializer and plain REST will be cheaper to build, cheaper to explain, and cheaper to maintain. The standard is worth deciding on its own merits; this particular implementation should not be the reason to adopt it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource