PHPackages                             brainstud/json-api-resource - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. brainstud/json-api-resource

ActiveLibrary[API Development](/categories/api)

brainstud/json-api-resource
===========================

Build JSON:API compliant Laravel API resources

v5.1.0(2mo ago)05.9k—1.5%[2 PRs](https://github.com/brainstudnl/json-api-resource/pulls)MITPHPPHP ^8.5

Since Jul 19Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/brainstudnl/json-api-resource)[ Packagist](https://packagist.org/packages/brainstud/json-api-resource)[ RSS](/packages/brainstud-json-api-resource/feed)WikiDiscussions main Synced yesterday

READMEChangelog (10)Dependencies (12)Versions (50)Used By (0)

JSON:API Resource for Laravel
=============================

[](#jsonapi-resource-for-laravel)

Make your Laravel API [JSON:API](https://jsonapi.org/) compliant with the `Brainstud\JsonApi` package.

Table of contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Usage](#usage)
- [Relationships](#relationships)
- [Resource depth](#resource-depth)
- [Exception handler](#exception-handler)
- [Example](#example-usage)
- [Deprecated `register` method](#defining-resources-via-the-register-method)
- [Tweaking responses](#tweak-response)
- [License](#license)

Installation
------------

[](#installation)

Require the package

```
composer require brainstud/json-api-resource
```

Usage
-----

[](#usage)

- Let your resource object extend from `JsonApiResource` instead of `JsonResource`.
- Set the type of your resource as a string in `$this->type`.
- For each part of your resource, define the matching `to{resourcePart}` method.

```
class Resource extends JsonApiResource
{
    protected string $type = 'resources';

    protected function toAttributes(Request $request): array
    {
        return [
            'field' => $this->resource->field,
            'other_field' => $this->resource->other_field,
        ];
    }

    protected function toRelationships(Request $request): array
    {
        return [
            'relation' => ['relationMethod', Relation::class],
        ];
    }

    protected function toLinks(Request $request): array
    {
        return [
            'type_of_link' => ['href' => 'link'],
        ];
    }

    protected function toMeta(Request $request): array
    {
        return [
            'meta' => 'data',
        ];
    }
}
```

Relationships
-------------

[](#relationships)

[JSON:API: Includes](https://jsonapi.org/format/#fetching-includes)For the relationships to be included they need to be loaded. This can be done by implementing a `?include` parameter or using [spatie/laravel-query-builder](https://spatie.be/docs/laravel-query-builder/v3/introduction).

Resource depth
--------------

[](#resource-depth)

The resource depth has a default of 2. This can be changed by passing an array to the resource where the second item is the required resource depth. In the following example we use a depth of 3:

```
public function show(ShowCourseRequest $request, Course $course)
{
    $query = (new CoursesQueryBuilder)->find($course->id);
    return new CourseResource([$query, 3]);
}
```

Which allows us to ask for an include nested 3 levels deep: `/courses/{identifier}?include=content,content.answers,content.answers.feedback`

Exception handler
-----------------

[](#exception-handler)

This package contains an exception handler to render exceptions as JSON:API error messages. Either use this handler directly by editing your `app.php` and registering this singleton

```
// app.php
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    \Brainstud\JsonApi\Handlers\JsonApiExceptionHandler::class
);
```

Or register your own exception handler and delegate the render to the `JsonApiExceptionHandler::render` method.

```
// app.php
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

// handler.php
public function render($request, Throwable $exception)
{
    if ($request->wantsJson()) {
        return (new JsonApiExceptionHandler($this->container))->render($request, $exception);
    }

    return parent::render($request, $exception);
}
```

### Return error response

[](#return-error-response)

There are multiple ways to return an error page

```
// Throw an exception that will be handled by the JsonApiExceptionHandler
throw new UnprocessableEntityHttpException();

// Return a defined error response
return (new UnprocessableEntityError)->response();

// Return a custom error response
return ErrorResponse::make(new DefaultError(
    'PROCESSING_ERROR',
    'Could not save item',
    'An error occurred during saving of the item'
), Response::HTTP_INTERNAL_SERVER_ERROR);
```

Example usage
-------------

[](#example-usage)

```
// Course.php

/**
 * @property int $id
 * @property string $title
 * @property string $description
 * @property Carbon $created_at
 * @property Collection $enrollments
 */
class Course extends Model
{
    protected $fillable = [
        'title',
        'description',
    ];

    public function enrollments(): HasMany
    {
        return $this->hasMany(Enrollment::class);
    }
}

// CourseResource.php

/**
 * @property Course $resource
 */
class CourseResource extends JsonApiResource
{
    protected string $type = 'courses';

    protected function toAttributes(Request $request): array
    {
        return [
            'title' => $this->resource->title,
            'description' => $this->resource->description,
            'created_at' => $this->resource->created_at->format('c'),
        ];
    }

    protected function toRelationships(Request $request): array
    {
        return [
            'enrollments' => ['enrollments', EnrollmentResourceCollection::class],
        ];
    }

    protected function toLinks(Request $request): array
    {
        return [
            'view' => ['href' => $this->resource->getShowUrl()],
        ];
    }

    protected function toMeta(Request $request): array
    {
        return [
            'enrollments' => $this->resource->enrollments->count(),
        ];
    }
}

// CoursesController.php

class CoursesController
{
    public function index(IndexCoursesRequest $request)
    {
        $query = (new CoursesQueryBuilder)->jsonPaginate();
        return new CourseResourceCollection($query);
    }

    public function show(ShowCourseRequest $request, Course $course)
    {
        $query = (new CoursesQueryBuilder)->find($course->id);
        return new CourseResource($query);
    }
}
```

Defining resources via the `register` method
--------------------------------------------

[](#defining-resources-via-the-register-method)

In the previous version of the package, you would have to define the resource structure via a register method. This is still possible, but it is **deprecated** and will be removed in a later version.

To use this way of defining a resource, simply define a `register` method in your resource:

```
protected function register(): array
{
    return [
        'id' => $this->resource->identifier,
        'type' => 'object_type',
        'attributes' => [
            'field' => $this->resource->field,
            'other_field' => $this->resource->other_field,
        ],
        'relationships' => [
            'items' => ['items', ItemsResourceCollection::class],
            'item' => ['item', ItemResource::class],
        ],
        'meta' => [
            'some_data' => 'some value',
        ],
        'links' => [
            'some_key' => 'some link',
        ],
    ];
}
```

Tweak response
--------------

[](#tweak-response)

The `register` method doesn't have access to `$request` like `toArray` of `JsonResource` has. If you want to manipulate the response based on the request this can be done by overriding the `addToResponse` method.

```
protected function addToResponse($request, $response): array
{
    if ($this->requestWantsMeta($request, 'data')
        && ($data = $this->getData())
    ) {
        $response['meta']['data'] = $data;
    }

    return $response;
}
```

License
-------

[](#license)

JsonApi is open-sourced software licensed under the [MIT Licence](LICENSE)

###  Health Score

54

—

FairBetter than 96% of packages

Maintenance85

Actively maintained with recent releases

Popularity22

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity82

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 72% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~44 days

Recently: every ~25 days

Total

40

Last Release

78d ago

Major Versions

v1.0.6 → v2.0.02021-12-16

v2.1.0 → v3.0.02022-02-09

v3.1.11 → v4.0.02024-04-05

v4.x-dev → v5.0.02026-03-02

PHP version history (6 changes)v1.0.0PHP ^8.0

v1.0.3PHP ^7.4 || ^8.0

v3.1.5PHP 8.0

v3.1.9PHP ^8.1

v4.0.3PHP ^8.2

v5.0.0PHP ^8.5

### Community

Maintainers

![](https://www.gravatar.com/avatar/59a4113e79a9d7d16cf8857d4f256eabf9cd2a546b692a9ffb02b2ab86799567?d=identicon)[Diondb](/maintainers/Diondb)

![](https://www.gravatar.com/avatar/dcdd9cd8db3f43b6b6c140ff30840863339e17180bd3ea6745c9b151136610d7?d=identicon)[brdv](/maintainers/brdv)

---

Top Contributors

[![brdv](https://avatars.githubusercontent.com/u/44579145?v=4)](https://github.com/brdv "brdv (54 commits)")[![frapidev](https://avatars.githubusercontent.com/u/42831804?v=4)](https://github.com/frapidev "frapidev (12 commits)")[![Diondb](https://avatars.githubusercontent.com/u/84917298?v=4)](https://github.com/Diondb "Diondb (4 commits)")[![Reinand](https://avatars.githubusercontent.com/u/133855289?v=4)](https://github.com/Reinand "Reinand (3 commits)")[![Fafabian](https://avatars.githubusercontent.com/u/15800221?v=4)](https://github.com/Fafabian "Fafabian (2 commits)")

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/brainstud-json-api-resource/health.svg)

```
[![Health](https://phpackages.com/badges/brainstud-json-api-resource/health.svg)](https://phpackages.com/packages/brainstud-json-api-resource)
```

###  Alternatives

[statamic/cms

The Statamic CMS Core Package

4.8k3.6M986](/packages/statamic-cms)[darkaonline/l5-swagger

OpenApi or Swagger integration to Laravel

3.0k37.6M134](/packages/darkaonline-l5-swagger)[knuckleswtf/scribe

Generate API documentation for humans from your Laravel codebase.✍

2.3k14.2M63](/packages/knuckleswtf-scribe)[statamic-rad-pack/runway

Eloquently manage your database models in Statamic.

135224.7k7](/packages/statamic-rad-pack-runway)[scriptdevelop/whatsapp-manager

Paquete para manejo de WhatsApp Business API en Laravel

783.8k](/packages/scriptdevelop-whatsapp-manager)[duncanmcclean/statamic-cargo

Comprehensive e-commerce addon for Statamic. Build bespoke e-commerce sites without the complexity.

3416.7k](/packages/duncanmcclean-statamic-cargo)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
