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

Laravel5 Json Api Laravel Package

nilportugues/laravel5-json-api

Laravel 5 package for building APIs that follow the JSON API specification (jsonapi.org). It transforms Eloquent models and collections into spec-compliant responses, including relationships, pagination, errors, and content negotiation, with minimal setup and configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up. The package is a Laravel 5 service-provider wrapper around nilportugues/php-json-api, so installation is the usual three-step dance:

composer require nilportugues/laravel5-jsonapi
// config/app.php
'providers' => [
    // ...
    NilPortugues\Laravel5\JsonApi\JsonApiServiceProvider::class,
],
php artisan vendor:publish --provider="NilPortugues\Laravel5\JsonApi\JsonApiServiceProvider"

Look here first:

  • config/jsonapi.php — global settings (JSON API version, top-level jsonapi block, default links/meta).
  • vendor/nilportugues/laravel5-jsonapi/src/ — the service provider and any response/controller helpers the wrapper adds.
  • vendor/nilportugues/php-json-api/src/ — the actual serializer and JsonApiTransformer base class. This is where the real behavior lives; the Laravel package is mostly glue.

First use case — serialize a collection. Define one transformer per resource type and hand it to the serializer from a controller:

namespace App\Http\Transformers;

use NilPortugues\Api\JsonApi\JsonApiTransformer;

class PostTransformer extends JsonApiTransformer
{
    public function getName()
    {
        return 'posts'; // JSON API "type"
    }

    public function getAttributes($post)
    {
        return [
            'title' => $post->title,
            'body'  => $post->body,
        ];
    }
}
use NilPortugues\Api\JsonApi\JsonApiSerializer;

public function index()
{
    $posts = Post::with('author')->paginate(20);
    $serializer = new JsonApiSerializer(new PostTransformer());

    return response()->json($serializer->serialize($posts));
}

That single transformer gives you the core spec features for free: type/id/attributes shape, data vs included, and relationship linkage.

Implementation Patterns

One transformer per resource, named after the pluralized type. Keep getName() stable and lowercase (posts, blog-comments). Clients key off it in relationships, so renaming it is a breaking change.

Controller shape. The package doesn't enforce a controller convention; the common pattern is thin controllers that eager-load, serialize, and return response()->json(...). If the installed version exposes a response helper or base controller, use it — otherwise the JsonApiSerializer directly is the stable interface.

public function show($id)
{
    $post = Post::with('author', 'comments')->findOrFail($id);
    $serializer = new JsonApiSerializer(new PostTransformer());

    return response()->json($serializer->serialize($post));
}

Relationships and compound documents. Declare relationships in getRelationships(); the serializer will emit relationships linkage and, when the request includes ?include=author, populate the top-level included array. The helper names on the base class vary by version — check JsonApiTransformer for the exact hasOne/hasMany (or Relationship object) API before writing many transformers.

public function getRelationships($post)
{
    return [
        'author'   => $this->hasOne('users', (string) $post->author_id),
        'comments' => $this->hasMany('comments', $post->comments->pluck('id')->map('strval')->all()),
    ];
}

Pagination. Pass an Illuminate\Pagination\LengthAwarePaginator into serialize() so the serializer can emit links/meta. Don't paginate in the transformer — pagination is a serialization concern. As of 2.4.7, the page size is taken from Eloquent's paginator by default, so Post::paginate(20) produces links that respect 20 without any extra wiring. If you were previously passing an explicit page size or overriding it, verify the number still matches after upgrading — the paginator's value now wins.

Sparse fieldsets and includes. The serializer honors fields[posts]=title,body&include=author when the transformer exposes those names. This is the main "freebie" that justifies the package over hand-rolling resources.

Errors as JSON API. For 4xx/5xx, return a spec-shaped error document rather than Laravel's default {message, exception}. Either hook the package's exception handling (if present in your installed version) or normalize in app/Exceptions/Handler.php:

protected function invalidJson($request, ValidationException $e)
{
    return response()->json([
        'errors' => collect($e->errors())->flatMap(function ($messages, $field) {
            return array_map(fn ($m) => [
                'status' => '422',
                'source' => ['pointer' => "/data/attributes/{$field}"],
                'title'  => 'Validation failed',
                'detail' => $m,
            ], $messages);
        })->values(),
    ], 422, ['Content-Type' => 'application/vnd.api+json']);
}

Reuse via the container. Bind a singleton or factory for the serializer, or inject transformers where they're shared. This keeps controllers from constructing serializers repeatedly and makes it easy to swap a transformer in tests.

Testing. Assert against the JSON structure, not the exact string:

$response->assertJsonStructure([
    'data' => [['type', 'id', 'attributes' => ['title', 'body']]],
]);

Gotchas and Tips

It is effectively abandoned. Last release August 2016, zero dependents. It targets Laravel 5.x and PHP 5.5–7.0 idioms. On Laravel 6+ or PHP 8 you will hit incompatibilities (service-provider signatures, str_* helpers, type hints). Pin laravel/framework and PHP versions, or budget a fork if you're maintaining it.

Pagination page-size semantics differ by patch level. 2.4.7 changed the default so Eloquent's per-page value is used. On older releases the size could come from elsewhere, so paginated links from a paginate(20) call may shift after upgrade. Assert the page[size]/page[number] links in a test if clients depend on them, and pin the version if you can't retest.

id values must be strings. JSON API 1.0 requires id to be a string. Cast model keys with (string) in transformers; integers will produce technically invalid documents and break strict clients.

Empty to-many relationships must be [], not null. If $post->comments is an empty collection and you pass null, you'll emit "data": null, which means "no relationship" in JSON API — a different thing from "empty relationship."

included silently disappears without linkage. If getRelationships() doesn't declare a relationship, ?include= for it is ignored. When compound documents look empty, inspect the transformer before debugging the request.

Watch N+1 on includes. The serializer iterates relationships per record. Always with() the relationships you expose in getRelationships(), even when the client didn't ask for include.

Content-Type matters. Spec-compliant clients require application/vnd.api+json. response()->json() defaults to application/json; set the header explicitly (or via middleware) if you care about strict conformance.

Sparse fieldsets hide things unexpectedly. ?fields[posts]=title will strip body and any relationship linkage not listed. When an attribute "goes missing" in one environment, check for a client-supplied fields parameter before chasing code.

Config cache quirk. config/jsonapi.php is read at boot. After editing it in a deployed environment, run php artisan config:cache (and config:clear when debugging) or your changes won't take effect.

Serializer exceptions are opaque. Malformed transformers (e.g. missing getName(), non-scalar attribute values that can't be encoded) throw from deep inside php-json-api. Wrap serialization in a try/catch during development and log json_last_error_msg() when output looks truncated.

Extension points worth knowing. You can subclass JsonApiTransformer for a project-wide base (shared timestamps, IDs, links), swap in a custom serializer, or override the package's container binding to control versioning/meta centrally. The middleware stack is also a reasonable place to enforce the Accept header or inject top-level meta (e.g. request IDs) across every response.

Prefer upgrading before extending. Because the dependency is frozen, any significant extension is maintenance debt. If you're starting today, weigh the cost of adopting a maintained JSON API implementation against the value of the transformer/serializer ergonomics this package provides — the concepts (transformers, includes, sparse fieldsets) port cleanly to a hand-rolled or actively maintained equivalent.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo