PHPackages                             coder-sapient/json-api-document-builder - 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. coder-sapient/json-api-document-builder

ActiveLibrary[API Development](/categories/api)

coder-sapient/json-api-document-builder
=======================================

Json Api Document Builder

v1.0.0(4y ago)382MITPHPPHP &gt;=8.0

Since Mar 28Pushed 4y ago1 watchersCompare

[ Source](https://github.com/coder-sapient/json-api-document-builder)[ Packagist](https://packagist.org/packages/coder-sapient/json-api-document-builder)[ RSS](/packages/coder-sapient-json-api-document-builder/feed)WikiDiscussions main Synced 1mo ago

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

Json Api Document Builder
-------------------------

[](#json-api-document-builder)

This library resolve Query Object to [JSON:API](https://jsonapi.org/format/#document-structure) Documents.

Features
--------

[](#features)

- Pagination, sorting, filtering with the following operators (`eq`, `neq`, `gt`, `lt`, `gte`, `lte`, `like`).
- Multiple nested paths resource inclusion (e.g. `article, article.author, article.comments.user`).
- Async resource inclusion (Guzzle Promises/A+).
- Caching resolved resources.
- Fully Unit tested

Request examples:

```
GET /api/v1/articles/{id}?include=author
GET /api/v1/articles?include=author,comments.user&page=1&per_page=15
GET /api/v1/articles?sort=id,-title // sort id in asc, title in desc
GET /api/v1/articles?filter[id]=100,101&filter[title][like]=value

```

Requirements
------------

[](#requirements)

- PHP version &gt;=8.0

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

[](#installation)

Use composer to install the package:

```
composer require coder-sapient/json-api-document-builder

```

Basic Usage
-----------

[](#basic-usage)

Controller action example:

```
final class ShowArticleAction
{
    public function __construct(private SingleDocumentBuilder $builder)
    {
    }

    public function __invoke(ShowArticleRequest $request): string
    {
        try {
            $document = $this->builder->build($request->toQuery());
        } catch (JsonApiException $e) {
            return json_encode($e->jsonApiErrors());
        }

        return json_encode($document);
    }
}
```

You can add the following traits to your request classes:

- [SingleDocumentRequest](/src/Request/SingleDocumentRequest.php): For documents about a single top-level resource.
- [DocumentsRequest](/src/Request/DocumentsRequest.php): For documents about a collection of top-level resources.

### SingleDocumentRequest

[](#singledocumentrequest)

```
final class ShowArticleRequest extends Request
{
    use SingleDocumentRequest;

    protected function resourceId(): string
    {
        // return from URL ~/articles/{resourceId}
    }

    protected function resourceType(): string
    {
        return 'articles';
    }

    protected function acceptableIncludes(): array
    {
        return ['author', 'comments', 'comments.user'];
    }
}
```

MethodDescription`resourceId()`Returns the resource id, which should be taken from the URL, for example.`resourceType()`Returns the resource type that defines the [ResourceResolver](#ResourceResolver)`acceptableIncludes()`Returns a list of acceptable relationship names to include`toQuery()`Returns the [SingleDocumentQuery](/src/Query/SingleDocumentQuery.php) object that can be handled by [SingleDocumentBuilder](#SingleDocumentBuilder)### DocumentsRequest

[](#documentsrequest)

```
final class ListArticlesRequest extends Request
{
    use DocumentsRequest;

    protected function resourceType(): string
    {
        return 'articles';
    }

    protected function acceptableSorting(): array
    {
        return ['title', 'created_at'];
    }

    protected function acceptableIncludes(): array
    {
        return ['author', 'comments', 'comments.user'];
    }

    protected function acceptableFilters(): array
    {
        return [
            'author_id' => ['eq'],
            'title' => ['eq', 'like'],
        ];
    }
}
```

MethodDescription`resourceType()`Returns the resource type that defines the [ResourceResolver](#ResourceResolver)`acceptableIncludes()`Returns a list of acceptable relationship names to include`acceptableSorting()`Returns a list of acceptable rows for sorting`acceptableFilters()`Returns a list of acceptable filters that can be applied to resource collection`toQuery()`Returns the [DocumentsQuery](/src/Query/DocumentsQuery.php) object that can be handled by [DocumentsBuilder](#DocumentsBuilder)Builder
-------

[](#builder)

To initialize [Builder](/src/Builder/Builder.php), you need to provide instances of [ResourceResolverFactory](#Factory) and [ResourceCache](#ResourceCache):

MethodDescription`buildIncludes(Includes $includes, ResourceCollection $resources)`Returns the included collection of resource objects### SingleDocumentBuilder

[](#singledocumentbuilder)

The [SingleDocumentBuilder](/src/Builder/SingleDocumentBuilder.php) extends `Builder`:

MethodDescription`build(SingleDocumentQuery $query)`Returns a document with single top-level resource### DocumentsBuilder

[](#documentsbuilder)

The [DocumentsBuilder](/src/Builder/DocumentsBuilder.php) extends `Builder`:

MethodDescription`build(DocumentsQuery $query)`Returns a document with top-level resourcesResolver
--------

[](#resolver)

### Factory

[](#factory)

The [ResourceResolverFactory](/src/Factory/ResourceResolverFactory.php) is a factory that return a [ResourceResolver](#ResourceResolver) by resource type.

```
interface ResourceResolverFactory
{
    /**
     * @throws ResourceResolverNotFoundException
     */
    public function make(string $resourceType): ResourceResolver;
}
```

There is a basic implementation [InMemoryResourceResolverFactory](/src/Factory/InMemoryResourceResolverFactory.php):

```
$factory = new InMemoryResourceResolverFactory();

$factory->add(
    'articles', // resource type
    new ArticleResourceResolver()
);
$factory->add(
    'users',
    new AuthorResourceResolver()
);
$factory->add(
    'comments',
    new CommentResourceResolver()
);

$builder = new SingleDocumentBuilder($factory, new InMemoryResourceCache());

$singleDocument = $builder->build($request->toQuery());
```

### ResourceResolver

[](#resourceresolver)

The builder use instances of [ResourceResolver](/src/Resolver/ResourceResolver.php) to find resources by ids or query criteria.

```
interface ResourceResolver
{
    /**
     * @param DocumentsQuery $query
     *
     * @return ResourceObject[]
     */
    public function resolveMany(DocumentsQuery $query): array;

    /**
     * @param SingleDocumentQuery $query
     *
     * @return ResourceObject|null
     */
    public function resolveOne(SingleDocumentQuery $query): ?ResourceObject;

    /**
     * @param string ...$resourceIds
     *
     * @return ResourceObject[]|PromiseInterface
     */
    public function resolveByIds(string ...$resourceIds): array|PromiseInterface;
}
```

When resolving a collection of top-level resources, it will provide a query criteria consisting of filters, orders, pagination. You need to match criteria with your query builder (Doctrine, Eloquent, etc.).

The builder can accept Guzzle Promises when trying to include related resources and load them async.

### PaginationResolver

[](#paginationresolver)

```
interface PaginationResolver
{
    /**
     * @param DocumentsQuery $query
     *
     * @return PaginationResponse
     */
    public function paginate(DocumentsQuery $query): PaginationResponse;
}
```

If the resource resolver implements [PaginationResolver](/src/Resolver/PaginationResolver.php), the builder will add top-level `Links` and `Meta` objects to the resulting document.

```
{
  "links": {
    "first": "http://localhost/api/v1/articles?page=1&per_page=15",
    "prev": "http://localhost/api/v1/articles?page=1&per_page=15",
    "next": "http://localhost/api/v1/articles?page=2&per_page=15",
    "last": "http://localhost/api/v1/articles?page=3&per_page=15",
  },
  "meta": {
    "total": 45,
    "page": 1,
    "per_page": 15,
    "last_page": 3
  }
}

```

### ResourceCache

[](#resourcecache)

The builder caches all resolved resources using instance of [ResourceCache](/src/Cache/ResourceCache.php).

```
interface ResourceCache
{
    /**
     * @param string $key
     *
     * @return ResourceObject|null
     */
    public function getByKey(string $key): ?ResourceObject;

    /**
     * @return ResourceObject[]
     */
    public function getByKeys(string ...$keys): array;

    /**
     * @param JsonApiQuery $query
     *
     * @return ResourceObject[]
     */
    public function getByQuery(JsonApiQuery $query): array;

    /**
     * @param ResourceObject ...$resources
     *
     * @return void
     */
    public function setByKeys(ResourceObject ...$resources): void;

    /**
     * @param JsonApiQuery $query
     * @param ResourceObject ...$resources
     *
     * @return void
     */
    public function setByQuery(JsonApiQuery $query, ResourceObject ...$resources): void;

    /**
     * @param string ...$keys
     *
     * @return void
     */
    public function removeByKeys(string ...$keys): void;

    /**
     * @param string ...$resourceTypes
     *
     * @return void
     */
    public function removeByTypes(string ...$resourceTypes): void;

    /**
     * @return void
     */
    public function flush(): void;
}
```

There is a basic implementation [InMemoryResourceCache](/src/Cache/InMemoryResourceCache.php). If you don't need caching, use [NullableResourceCache](/src/Cache/NullableResourceCache.php).

License
-------

[](#license)

The MIT License (MIT). Please see [LICENSE](LICENSE) for more information.

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 54.4% 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

Unknown

Total

1

Last Release

1512d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/24546243437da1ee22d6b4311fad03f41ac9d9f431e4e044ecb38c7948d5584f?d=identicon)[coder-sapient](/maintainers/coder-sapient)

---

Top Contributors

[![yarikhalupiak](https://avatars.githubusercontent.com/u/25246952?v=4)](https://github.com/yarikhalupiak "yarikhalupiak (56 commits)")[![coder-sapient](https://avatars.githubusercontent.com/u/95776172?v=4)](https://github.com/coder-sapient "coder-sapient (46 commits)")[![oleksiikhr](https://avatars.githubusercontent.com/u/14747569?v=4)](https://github.com/oleksiikhr "oleksiikhr (1 commits)")

---

Tags

apiJSON-API

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/coder-sapient-json-api-document-builder/health.svg)

```
[![Health](https://phpackages.com/badges/coder-sapient-json-api-document-builder/health.svg)](https://phpackages.com/packages/coder-sapient-json-api-document-builder)
```

###  Alternatives

[saloonphp/saloon

Build beautiful API integrations and SDKs with Saloon

2.4k9.6M468](/packages/saloonphp-saloon)[neomerx/json-api

Framework agnostic JSON API (jsonapi.org) implementation

7373.6M27](/packages/neomerx-json-api)[art4/json-api-client

JSON API client

139791.3k7](/packages/art4-json-api-client)[friendsofcake/crud-json-api

Listener for building CakePHP Crud APIs following the JSON API specification.

58445.4k3](/packages/friendsofcake-crud-json-api)[cloudcreativity/json-api-testing

PHPUnit test helpers to check JSON API documents.

141.6M3](/packages/cloudcreativity-json-api-testing)[dystcz/lunar-api

Dystore API layer for Lunar e-commerce package

411.1k3](/packages/dystcz-lunar-api)

PHPackages © 2026

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