PHPackages                             chriskelemba/jsonapi-response - 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. chriskelemba/jsonapi-response

ActiveLibrary[API Development](/categories/api)

chriskelemba/jsonapi-response
=============================

JSON:API response builder for Laravel

v1.0.7(3mo ago)012MITPHPPHP ^8.2

Since Feb 3Pushed 3mo agoCompare

[ Source](https://github.com/chriskelemba/jsonapi-response)[ Packagist](https://packagist.org/packages/chriskelemba/jsonapi-response)[ RSS](/packages/chriskelemba-jsonapi-response/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (3)Versions (8)Used By (0)

JSON:API Response for Laravel
=============================

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

[![JSON:API Response banner](assets/jsonapi-response-hero.svg)](assets/jsonapi-response-hero.svg)

A lightweight JSON:API response helper for Laravel with pagination, links, query helpers (sort/filter/include/fields), and consistent response formatting.

Install
-------

[](#install)

```
composer require chriskelemba/jsonapi-response
```

Laravel package auto-discovery will register the service provider automatically.

### Publish config (optional)

[](#publish-config-optional)

```
php artisan vendor:publish --tag=jsonapi-config
```

You do not need to publish config to use the package.

Quick Start
-----------

[](#quick-start)

### Collection + Pagination

[](#collection--pagination)

```
use ChrisKelemba\ResponseApi\JsonApi;

return JsonApi::response(Book::query(), 'books', request());
```

Passing a `Builder` or `Relation` now auto-paginates by default using `?page[number]` and `?page[size]`.

To override page size:

- Per request: `?page[size]=25`
- Default for all requests: set `pagination.default_size` in `config/jsonapi.php`

To omit pagination for a query response:

- Per request: `?page[disable]=true` (or `?page[size]=0`)
- Globally: set `pagination.enabled` to `false`
- Per endpoint: call `->get()` and pass the collection to `JsonApi::response()`

### Drop-in Response Replacement

[](#drop-in-response-replacement)

Keep your existing controller logic and only replace the return line:

```
$books = Book::all();

return JsonApi::response($books, 'books', request());
```

### Single Resource

[](#single-resource)

```
return JsonApi::response($book, 'books', request());
```

### Create (201 + Location header)

[](#create-201--location-header)

```
$book = Book::create($validated);

return JsonApi::response($book, 'books', null, 201);
```

### Delete (204)

[](#delete-204)

```
return JsonApi::response(null, null, null, 204);
```

Query Helpers (JSON:API recommendations)
----------------------------------------

[](#query-helpers-jsonapi-recommendations)

Apply sort, filter, include, and sparse fieldsets using a safe allow‑list.

```
$query = JsonApi::applyQuery(Book::query(), request(), 'books', [
    'allowed_sorts' => ['created_at', 'title'],
    'allowed_filters' => ['author', 'genre'],
    'allowed_includes' => ['author'],
    'allowed_fields' => ['title', 'author', 'created_at'],
]);

$books = $query->paginate(15);

return JsonApi::response($books, 'books', request());
```

### Minimal Controller Setup

[](#minimal-controller-setup)

For low-boilerplate controllers, use model-aware helpers:

```
use App\Models\User;
use ChrisKelemba\ResponseApi\JsonApi;
use Illuminate\Http\Request;

public function index(Request $request)
{
    $query = JsonApi::applyModelQuery(User::query(), $request, 'users', [
        'allowed_includes' => ['tasks', 'roles', 'permissions'],
    ]);

    $users = JsonApi::paginateQuery($query, $request);

    return JsonApi::response($users, 'users', $request);
}
```

`applyModelQuery()` auto-derives allow-lists from the model and excludes hidden attributes.

Supported query params:

- `?sort=created_at` or `?sort=-created_at`
- `?filter[author]=Jane`
- `?include=author,comments`
- `?max_include=100`
- `?max_include[comments]=50`
- `?max_include[tasks.subTasks]=5`
- `?fields[books]=title,author`
- `?page[number]=2&page[size]=25`

You can also pass the query directly to `response()` and let the package paginate automatically:

```
$query = JsonApi::applyModelQuery(User::query(), $request, 'users');

return JsonApi::response($query, 'users', $request);
```

Relationships + Included
------------------------

[](#relationships--included)

If a relationship is **loaded**, the package will add a JSON:API `relationships` object automatically.

To load and include related resources without controller code, use `?include=`:

```
GET /api/book-authors?include=books

```

Nested includes are supported via dot notation:

```
GET /api/users/1?include=tasks.subTasks

```

This will:

- Load `books` automatically.
- Add `relationships.books` to each author.
- Add `included` resources for the related books (unless `include_compound_documents` is disabled).
- If `relationships.links_for_includes` is enabled, relationship links are emitted even when the relation isn't loaded.

### Limit Included Size

[](#limit-included-size)

If a relationship is large, you can cap how many related resources are serialized in `relationships` and `included`:

```
GET /api/book-authors/51?include=books&max_include=100

```

Nested per-path include limits are supported:

```
GET /api/users/1?include=tasks.subTasks&max_include[tasks]=10&max_include[tasks.subTasks]=3

```

Notes:

- This limits the **response size** only. It does not change how many related rows are loaded from the database.
- For large related sets, prefer paging the related collection via a filtered endpoint.
- There is no default include cap unless you set `query.max_include` or pass `max_include` in the request.

### Relationship Pagination (JSON:API standard)

[](#relationship-pagination-jsonapi-standard)

`include` is for sideloading, not true paging of related collections.

Use the related endpoint for paging:

```
GET /api/book-authors/51/books?page[number]=1&page[size]=10

```

### Pagination Links Preserve Query

[](#pagination-links-preserve-query)

Pagination links (`first`, `next`, `last`) keep your current query parameters (e.g. `filter`, `include`, `fields`).

Example (controller stays simple):

```
public function index(Request $request)
{
    $authors = BookAuthor::query()->paginate(15);

    return JsonApi::response($authors, 'book-authors', $request);
}
```

Error Responses
---------------

[](#error-responses)

```
use ChrisKelemba\ResponseApi\JsonApi;

$errors = [
    JsonApi::error(
        422,
        'Validation Error',
        'The title field is required.',
        'VALIDATION_ERROR',
        ['pointer' => '/data/attributes/title'],
        ['field' => 'title'],
        'err_123',
        ['about' => 'https://api.example.com/errors/err_123']
    ),
];

return JsonApi::responseErrors($errors, 422);
```

Supported error members include `id`, `links`, `status`, `code`, `title`, `detail`, `source`, and `meta`.

### Validation Errors

[](#validation-errors)

```
use ChrisKelemba\ResponseApi\JsonApi;

return JsonApi::responseValidationErrors($validator->errors());
```

Configuration
-------------

[](#configuration)

All defaults are in `config/jsonapi.php`.

Key options:

- `transform_keys`: Enforce JSON:API member naming recommendations (camelCase + ASCII)
- `transform_recursive`: Apply key transform to nested payload structures
- `transform_attributes`: Transform keys inside resource `attributes` (set `false` to keep snake\_case like `parent_id`)
- `attributes.include_custom_primary_key`: Keep custom primary key columns in `attributes` (e.g. `ctg_id`) while still sending resource `id`
- `resource_links` / `relationship_links`: Include resource/relationship links
- `links.resource_base_path`: Prefix generated resource/relationship links (default: `api`)
- `method_override`: Enable `X-HTTP-Method-Override: PATCH`
- `include_jsonapi`: Include or suppress the top-level `jsonapi` object
- `include_compound_documents`: Include or suppress top-level `included`
- `eager_load_includes`: Toggle eager loading for `?include=`
- `relationships.links_for_includes`: Emit relationship links for `?include=` even if not loaded
- `query.*`: Allow‑lists for sort/filter/include/fields
- `query.allow_all_filters`: Allow any `filter[...]` key without an allow‑list (useful for dynamic APIs)
- `query.allow_all_sorts`: Allow any `sort` field without an allow‑list
- `query.allow_all_includes`: Allow any `include` without an allow‑list
- `query.allow_all_fields`: Allow any `fields[...]` without an allow‑list
- `errors.include_all_members`: Force error objects to include all standard members
- `pagination.enabled`: Auto-paginate `Builder`/`Relation` payloads in `JsonApi::response()`
- `pagination.allow_disable` / `pagination.disable_param`: Allow request-level opt-out (e.g. `page[disable]=true`)
- `pagination.number_param` / `pagination.size_param`: Page parameter names (`page.number` / `page.size` by default)
- `pagination.default_size` / `pagination.max_size`: Default and max page size limits

Notes
-----

[](#notes)

- Content-Type is set to `application/vnd.api+json`.
- ISO‑8601 timestamps are recommended.
- Eloquent hidden attributes are respected in serialized `attributes` (e.g. `password`, `remember_token`).

License
-------

[](#license)

MIT

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance82

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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 ~2 days

Total

6

Last Release

94d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3f2e2a68c016c348e7c39c94f5f72f095414bf225f047130fc01f379d2d05de7?d=identicon)[chriskelemba](/maintainers/chriskelemba)

---

Top Contributors

[![chriskelemba](https://avatars.githubusercontent.com/u/151122714?v=4)](https://github.com/chriskelemba "chriskelemba (18 commits)")

### Embed Badge

![Health badge](/badges/chriskelemba-jsonapi-response/health.svg)

```
[![Health](https://phpackages.com/badges/chriskelemba-jsonapi-response/health.svg)](https://phpackages.com/packages/chriskelemba-jsonapi-response)
```

###  Alternatives

[spatie/laravel-query-builder

Easily build Eloquent queries from API requests

4.4k26.9M220](/packages/spatie-laravel-query-builder)[essa/api-tool-kit

set of tools to build an api with laravel

52680.5k](/packages/essa-api-tool-kit)[esign/laravel-conversions-api

A laravel wrapper package around the Facebook Conversions API

69145.4k](/packages/esign-laravel-conversions-api)[surface/laravel-webfinger

A Laravel package to create an ActivityPub webfinger.

113.8k](/packages/surface-laravel-webfinger)

PHPackages © 2026

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