PHPackages                             bchalier/laravel-json-api - 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. bchalier/laravel-json-api

ActiveLibrary[API Development](/categories/api)

bchalier/laravel-json-api
=========================

A Lightweight JSON:API Resource for Laravel

1.6.0(1y ago)02.4kMITPHPPHP ^8.1

Since Mar 21Pushed 1y agoCompare

[ Source](https://github.com/bchalier/laravel-json-api)[ Packagist](https://packagist.org/packages/bchalier/laravel-json-api)[ RSS](/packages/bchalier-laravel-json-api/feed)WikiDiscussions master Synced yesterday

READMEChangelog (2)Dependencies (6)Versions (3)Used By (0)

JsonApi - Laravel Resource
==========================

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

A Lightweight [{JSON:API}](https://jsonapi.org/) Resource for Laravel.

[![example branch parameter](https://github.com/Ark4ne/laravel-json-api/actions/workflows/php.yml/badge.svg)](https://github.com/Ark4ne/laravel-json-api/actions/workflows/php.yml/badge.svg)[![codecov](https://camo.githubusercontent.com/2375585ff171702f205a807cc0209d667f325e38b7337682955a0279b1f71667/68747470733a2f2f636f6465636f762e696f2f67682f41726b346e652f6c61726176656c2d6a736f6e2d6170692f6272616e63682f6d61737465722f67726170682f62616467652e7376673f746f6b656e3d463758424c4147544450)](https://codecov.io/gh/Ark4ne/laravel-json-api)

Installation
============

[](#installation)

```
composer require ark4ne/laravel-json-api
```

Config
======

[](#config)

PathTypeDescription`describer.nullable``bool`For describer notation, defined if a value is nullable by default.`describer.date``string` datetime formatFor describer notation, defined default date time format.`describer.precision``int` \\ `null`For describer notation, decimal precision for float value. `null` for disable rounding.`describer.when-has``bool` \\ `string[]`For describer notation, Apply automatically whenHas condition on attributes.`relationship.when-included``bool`Allow to disabled by default the loading of relationship data.Usage
=====

[](#usage)

This package is a specialisation of Laravel's `JsonResource` class. All the underlying API's are still there, thus in your controller you can still interact with `JsonApiResource` classes as you would with the base `JsonResource` class

Request
-------

[](#request)

This package allows the reading and dynamic inclusion of resources that will be requested in the requests via the "include" parameter.
**@see** *[{json:api} fetching-includes](https://jsonapi.org/format/#fetching-includes)*

Resource attributes will also be filtered according to the "fields" parameter.
**@see** *[{json:api} fetching-fields](https://jsonapi.org/format/#fetching-sparse-fieldsets)*

You can also very simply validate your requests for a given resource via the rules `Rules\Includes` and `Rules\Fields`.

### Include validation

[](#include-validation)

```
use \Ark4ne\JsonApi\Requests\Rules\Includes;
use \Illuminate\Foundation\Http\FormRequest;

class UserFetchRequest extends FormRequest
{
    public function rules()
    {
        return [
            'include' => [new Includes(UserResource::class)],
        ]
    }
}
```

`Rules\Includes` will validate the `include` to exactly match the UserResource schema (determined by the relationships).

### Fields validation

[](#fields-validation)

```
use \Ark4ne\JsonApi\Requests\Rules\Fields;
use \Illuminate\Foundation\Http\FormRequest;

class UserFetchRequest extends FormRequest
{
    public function rules()
    {
        return [
            'fields' => [new Fields(UserResource::class)],
        ]
    }
}
```

`Rules\Fields` will validate the `fields` to exactly match the UserResource schema (determined by the attributes and relationships).

### Customize validation message

[](#customize-validation-message)

Trans keydefault`validation.custom.jsonapi.fields.invalid`The selected :attribute is invalid.`validation.custom.jsonapi.fields.invalid_fields`":resource" doesn ' t have fields ":fields".`validation.custom.jsonapi.fields.invalid_resource`":resource" doesn ' t exists.`validation.custom.jsonapi.includes.invalid`The selected :attribute is invalid.`validation.custom.jsonapi.includes.invalid_includes`":include" doesn ' t have relationship ":relation".Resource
--------

[](#resource)

**@see** *[{json:api} resource-type](https://jsonapi.org/format/#document-resource-objects)*

Implementable methods :

```
protected function toType(Request $request): string;

protected function toIdentifier(Request $request): int|string;

protected function toAttributes(Request $request): iterable;

protected function toRelationships(Request $request): iterable;

protected function toResourceMeta(Request $request): ?iterable;

protected function toMeta(Request $request): ?iterable;
```

Example:

```
use Ark4ne\JsonApi\Resources\JsonApiResource;
use Illuminate\Http\Request;

class UserResource extends JsonApiResource
{
    protected function toAttributes(Request $request): iterable
    {
        return [
            'name' => $this->name,
            'email' => $this->email,
        ];
    }

    protected function toResourceMeta(Request $request): ?iterable
    {
        return [
            'created_at' => $this->created_at->format(DateTimeInterface::ATOM),
            'updated_at' => $this->updated_at->format(DateTimeInterface::ATOM),
        ];
    }

    protected function toRelationships(Request $request): iterable
    {
        return [
            'posts' => PostResource::relationship(fn() => $this->posts, fn() => [
                'self' => "https://api.example.com/user/{$this->id}/relationships/posts",
                'related' => "https://api.example.com/user/{$this->id}/posts",
            ]),
            'comments' => CommentResource::relationship(fn() => $this->whenLoaded('comments')),
        ];
    }
}
```

### toType

[](#totype)

***@see** [{json:api} resource-type](https://jsonapi.org/format/#document-resource-object-identification)*

Returns resource type.

```
protected function toType(Request $request): string
{
    return 'user';
}
```

Default returns model class in kebab case : `App\Models\MyPost` =&gt; `my-post`

### toIdentifier

[](#toidentifier)

***@see** [{json:api} resource-identifier](https://jsonapi.org/format/#document-resource-object-identification)*

Returns resource identifier.

```
protected function toIdentifier(Request $request): int|string
{
    return $this->id;
}
```

Default returns model id.

### toAttributes

[](#toattributes)

***@see** [{json:api} resource-attributes](https://jsonapi.org/format/#document-resource-object-attributes)*

Returns resource attributes.

```
protected function toAttributes(Request $request): iterable
{
    return [
        'name' => $this->name,
        'email' => $this->email,
    ];
}
```

#### Laravel conditional attributes

[](#laravel-conditional-attributes)

***@see** [laravel: eloquent-conditional-attributes](https://laravel.com/docs/9.x/eloquent-resources#conditional-attributes)*

Support laravel conditional attributes.

```
protected function toAttributes(Request $request): array
{
    return [
        'name' => $this->name,
        'email' => $this->email,
        // with lazy evaluation
        'hash64' => fn() => base64_encode("{$this->id}-{$this->email}"),
        // Conditional attribute
        'secret' => $this->when($request->user()->isAdmin(), 'secret-value'),
        // Merging Conditional Attributes
        // use applyWhen insteadof mergeWhen for keep fields
        // useful for fields request rules validation
        $this->applyWhen($request->user()->isAdmin(), [
            'first-secret' => 123,
            'second-secret' => 456.789,
        ]),
    ];
}
```

#### Described attributes

[](#described-attributes)

***@see** [described notation](##described-notation)*

```
protected function toAttributes(Request $request): array
{
    return [
        'name' => $this->string(),
        // pass key to describer
        $this->string('email'),
        // with lazy evaluation
        'hash64' => $this->string(fn() => base64_encode("{$this->id}-{$this->email}")),
        // Conditional attribute
        $this->string('secret')->when($request->user()->isAdmin(), 'secret-value'),
        // Merging Conditional Attributes
        $this->applyWhen($request->user()->isAdmin(), [
            'first-secret' => $this->integer(fn() => 123),
            'second-secret' => $this->float(fn() => 456.789),
        ]),
    ];
}
```

### toRelationships

[](#torelationships)

***@see** [{json:api} resources-relationships](https://jsonapi.org/format/#document-resource-object-relationships)*

Returns resource relationships.

All relationships **must** be created with `ModelResource::relationship`. This allows the generation of the schema representing the resource and thus the validation of request includes.

If your relation should have been a collection created via the `::collection(...)` method, you can simply use `->asCollection()`.

If you want the relation data to be loaded only when it is present in the request include, you can use the `->whenIncluded()` method.

```
protected function toRelationships(Request $request): array
{
    return [
        'avatar' => AvatarResource::relationship($this->avatar),
        // with conditional relationship
        'administrator' => $this->when($request->user()->isAdmin(), UserResource::relationship(fn() => $this->administrator),
        // as collection, with conditional value
        'comments' => CommentResource::relationship(fn() => $this->whenLoaded('comments'))->asCollection(),
        // with relationship (allow to include links and meta on relation)
        'posts' => PostResource::relationship(fn() => $this->posts)->withLinks(fn() => [
            'self' => "https://api.example.com/user/{$this->id}/relationships/posts",
            'related' => "https://api.example.com/user/{$this->id}/posts",
        ])->asCollection(),
    ];
}
```

`toRelationships` must returns an array, keyed by string, of `JsonApiResource` or `JsonApiCollection`.

#### Laravel conditional relationships

[](#laravel-conditional-relationships)

***@see** [laravel: eloquent-conditional-relationships](https://laravel.com/docs/9.x/eloquent-resources#conditional-relationships)*

Support laravel conditional relationships.

```
protected function toRelationships(Request $request): array
{
    return [
        'avatar' => AvatarResource::relationship($this->avatar),
        // as collection, with condition
        'comments' => CommentResource::relationship(fn() => $this->whenLoaded('comments'))->asCollection(),
        // with relationship (allow to include links and meta on relation)
        'posts' => PostResource::relationship(fn() => $this->posts)
                ->asCollection(),
    ];
}
```

#### Described attributes

[](#described-attributes-1)

***@see** [described notation](##described-notation)*

```
protected function toRelationships(Request $request): array
{
    return [
        'avatar' => $this->one(AvatarResource::class),
        // custom relation name
        'my-avatar' => $this->one(AvatarResource::class, 'avatar'),
        // as collection, with condition
        'comments' => $this->many(CommentResource::class)
                           ->whenLoaded(),
        // with relationship (allow to include links and meta on relation)
        'posts' => $this->many(PostResource::class)
                ->links(fn() => [
                    'self' => "https://api.example.com/posts/{$this->resource->id}/relationships/posts",
                    'related' => "https://api.example.com/posts/{$this->resource->id}/posts",
                ])
                ->meta(fn() => [
                    'total' => $this->integer(fn() => $this->resource->posts()->count()),
                ]),
    ];
}
```

#### Relation links and meta

[](#relation-links-and-meta)

***@see** [{json:api}: relation-linkage](https://jsonapi.org/format/#document-resource-object-related-resource-links)*
***@see** [{json:api}: relation-meta](https://jsonapi.org/format/#document-resource-object-relationships)*

Returns links and meta for a relation.

```
protected function toRelationships(Request $request): array
{
    return [
        'posts' => PostResource::relationship(fn() => $this->posts)->withLinks(fn() => [
            // links
            'self' => "https://api.example.com/user/{$this->id}/relationships/posts",
            'related' => "https://api.example.com/user/{$this->id}/posts",
        ])->withMeta(fn() => [
            // meta
            'creator' => $this->name,
        ])
        ->asCollection(),
    ];
}
```

### toLinks

[](#tolinks)

***@see** [{json:api}: resource-linkage](https://jsonapi.org/format/#document-resource-object-links)*

Returns resource links.

```
protected function toLinks(Request $request): ?array
{
    return [
        'self' => route('api.user.show', ['id' => $this->id]),
    ];
}
```

### toResourceMeta

[](#toresourcemeta)

***@see** [{json:api}: resource-meta](https://jsonapi.org/format/#document-resource-objects)*
***@see** [{json:api}: document-meta](https://jsonapi.org/format/#document-meta)*

Returns resource meta.

```
protected function toResourceMeta(Request $request): ?iterable
{
    return [
        'created_at' => $this->created_at->format(DateTimeInterface::ATOM),
        'updated_at' => $this->updated_at->format(DateTimeInterface::ATOM),
    ];
}
```

### toMeta

[](#tometa)

***@see** [{json:api}: document-meta](https://jsonapi.org/format/#document-meta)*

Returns document meta.

```
protected function toMeta(Request $request): ?iterable
{
    return [
        "copyright": "Copyright 2022 My Awesome Api",
    ];
}
```

Collection
----------

[](#collection)

***@see** [laravel: resource-collection](https://laravel.com/docs/9.x/eloquent-resources#resource-collections)*

Collection are implemented in `JsonApiCollection`.

Usage is the same as laravel collections.

```
UserResource::collection(User::all()); // => JsonApiCollection
```

Described notation
------------------

[](#described-notation)

### Value methods

[](#value-methods)

MethodDescription`bool`Cast to boolean`integer`Cast to integer`float`Cast to float`string`Cast to string`date`Cast to date, allow to use custom format`array`Cast to array`mixed`Don't cast, return as is`enum`Get enum value.### Relation methods

[](#relation-methods)

MethodDescription`one`For relationship with a single value: `HasOne`, `BelongsTo`, ...`many`For relationship with many value: `HasMany`, `BelongsToMany`, ...### Enum

[](#enum)

Method `enum` allow to get enum value for backed enum or name for unit enum.

According to structure:

```
/// Role.php
enum Role {
    case ADMIN;
    case USER;
}
/// State.php
enum State:int {
    case ACTIVE = 1;
    case INACTIVE = 0;
}
/// User.php
class User extends Model
{
    $casts = [
        'role' => Role::class,
        'state' => State::class,
    ];
}
```

The following attributes resource:

```
// UserResource.php
protected function toAttributes(Request $request): array
{
    return [
        'status' => $this->enum(),
        'role' => $this->enum(),
    ];
}
```

Will return:

```
[
    "status": 1,
    "role": "ADMIN"
]
```

###  Health Score

31

—

LowBetter than 66% of packages

Maintenance38

Infrequent updates — may be unmaintained

Popularity16

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 99.3% 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 ~305 days

Total

2

Last Release

529d ago

### Community

Maintainers

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

---

Top Contributors

[![Ark4ne](https://avatars.githubusercontent.com/u/6144058?v=4)](https://github.com/Ark4ne "Ark4ne (150 commits)")[![bchalier](https://avatars.githubusercontent.com/u/17746418?v=4)](https://github.com/bchalier "bchalier (1 commits)")

---

Tags

laravelJSON-APIjsonapi.orglaravel jsonlaravel-resource

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/bchalier-laravel-json-api/health.svg)

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

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[illuminate/database

The Illuminate Database package.

2.8k54.9M11.6k](/packages/illuminate-database)[illuminate/auth

The Illuminate Auth package.

10528.2M1.2k](/packages/illuminate-auth)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

816333.6k3](/packages/defstudio-telegraph)[illuminate/routing

The Illuminate Routing package.

1419.2M3.0k](/packages/illuminate-routing)[api-platform/laravel

API Platform support for Laravel

58171.4k14](/packages/api-platform-laravel)

PHPackages © 2026

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