PHPackages                             sinemacula/laravel-api-toolkit - 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. sinemacula/laravel-api-toolkit

ActiveLibrary[API Development](/categories/api)

sinemacula/laravel-api-toolkit
==============================

A comprehensive Laravel toolkit for streamlined development of RESTful APIs

v1.16.4(5mo ago)218.6k—7.5%1[1 PRs](https://github.com/sinemacula/laravel-api-toolkit/pulls)Apache-2.0PHPPHP ^8.3CI passing

Since Aug 6Pushed 2w ago1 watchersCompare

[ Source](https://github.com/sinemacula/laravel-api-toolkit)[ Packagist](https://packagist.org/packages/sinemacula/laravel-api-toolkit)[ RSS](/packages/sinemacula-laravel-api-toolkit/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (10)Dependencies (32)Versions (80)Used By (0)

Laravel API Toolkit
===================

[](#laravel-api-toolkit)

[![Latest Stable Version](https://camo.githubusercontent.com/343cfbb5c5c6d9ba1a34b45a814b6f3162177ec0514f5db403344d0e4b35f5ad/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f73696e656d6163756c612f6c61726176656c2d6170692d746f6f6c6b69742e737667)](https://packagist.org/packages/sinemacula/laravel-api-toolkit)[![Build Status](https://github.com/sinemacula/laravel-api-toolkit/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/sinemacula/laravel-api-toolkit/actions/workflows/tests.yml)[![Quality Gates](https://github.com/sinemacula/laravel-api-toolkit/actions/workflows/quality-gates.yml/badge.svg?branch=master)](https://github.com/sinemacula/laravel-api-toolkit/actions/workflows/quality-gates.yml)[![Maintainability](https://camo.githubusercontent.com/17f8cd79d5b869a3d4865e3ddc2a61236ecae9166ad25eb27300aa3abddf5f33/68747470733a2f2f716c74792e73682f67682f73696e656d6163756c612f70726f6a656374732f6c61726176656c2d6170692d746f6f6c6b69742f6d61696e7461696e6162696c6974792e737667)](https://qlty.sh/gh/sinemacula/projects/laravel-api-toolkit)[![Code Coverage](https://camo.githubusercontent.com/00195082431ba469db90cfa93862a82ae38e9d242e0539806e64988bbd0605ac/68747470733a2f2f716c74792e73682f67682f73696e656d6163756c612f70726f6a656374732f6c61726176656c2d6170692d746f6f6c6b69742f636f7665726167652e737667)](https://qlty.sh/gh/sinemacula/projects/laravel-api-toolkit)[![Total Downloads](https://camo.githubusercontent.com/bdac223f789d582a9ab24c46957f31f0fa9193f58b4800442be093dd7dc41b20/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f73696e656d6163756c612f6c61726176656c2d6170692d746f6f6c6b69742e737667)](https://packagist.org/packages/sinemacula/laravel-api-toolkit)

The Laravel API Toolkit is a comprehensive package designed to simplify the development of RESTful APIs in Laravel. It provides tools to enhance API functionality, improve error handling, and ensure consistent data output, making API development faster and more reliable.

Features
--------

[](#features)

- **Exception Handling**: Implements a custom exception handler that captures and formats all exceptions for consistent API error responses, preserving the intended HTTP status codes.
- **Queryable Resources**: Resource schemas give fine-tuned control over which fields, filters, relations, and orderings are exposed via your API endpoints under a fail-closed allowlist posture, enhancing security and customization.
- **Data Repositories**: Abstracts database interactions into repositories to promote a cleaner and more maintainable codebase, with safe-by-default deferred writes (failed flushes retain records rather than dropping them) and per-query caching (each query is cached against its own fingerprint, so a cache hit performs zero database queries and a filtered read never returns the full table).
- **Data Resources**: Schema-driven resources ensure consistent presentation of data across different API endpoints, simplifying client-side data integration.
- **Services**: A composable service layer with immutable configuration, cross-cutting concerns (transactions, locking), and self-describing results.

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

[](#installation)

To install the Laravel API Toolkit, run the following command in your project directory:

```
composer require sinemacula/laravel-api-toolkit
```

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

[](#configuration)

After installation, publish the package configuration to customize it according to your needs:

```
php artisan vendor:publish --provider="SineMacula\ApiToolkit\ApiServiceProvider" --tag=config
```

This publishes `config/api-toolkit.php` to your application's config directory. The file is documented inline and covers exception rendering strategy, sensitive-key redaction, query-access posture, query parser limits, deferred write behaviour, middleware toggles, and more. Per-query repository caching is configured in the `sinemacula/laravel-repositories` package under `repositories.cache`.

Usage
-----

[](#usage)

### API Query Parser

[](#api-query-parser)

The `ApiQueryParser` sits behind the `ApiQuery` facade and is populated automatically by the `ParseApiQuery`middleware, which the service provider registers globally by default (controlled via `api-toolkit.parser.register_middleware`).

**Sparse fieldsets** - request only the fields you need for a given resource type:

```
GET /users?fields[user]=id,name,email
```

```
use SineMacula\ApiToolkit\Facades\ApiQuery;

$fields = ApiQuery::getFields('user'); // ['id', 'name', 'email']
```

**Filtering** - apply column-level filters by passing a URL-encoded JSON object of operator tokens:

```
GET /users?filters={"status":{"$eq":"active"},"created_at":{"$ge":"2024-01-01"}}
```

The `filters` value must be a JSON string; requests carrying a non-JSON value are rejected with a validation error.

Available built-in operator tokens: `$eq`, `$neq`, `$gt`, `$lt`, `$ge`, `$le`, `$like`, `$in`, `$between`, `$contains`, `$null`, `$notNull`.

**Sorting** - sort by one or more columns, with optional direction:

```
GET /users?order=last_name,first_name:desc
```

**Limit clamping** - client-supplied `?limit` values are silently clamped to the `api-toolkit.parser.max_limit`ceiling (default 100). Values exceeding the ceiling are reduced; the request is never rejected:

```
GET /users?limit=200   // clamped to 100
GET /users?limit=25    // honoured as-is
```

**Relation aggregates** - request counts, sums, or averages over declared relations:

```
GET /users?counts[user]=memberships,posts
GET /accounts?sums[account][transaction]=amount
GET /accounts?averages[account][order]=total
```

```
ApiQuery::getCounts('user');           // ['memberships', 'posts']
ApiQuery::getSums('account');          // ['transaction' => ['amount']]
ApiQuery::getAverages('account');      // ['order' => ['total']]
```

Cursor-based pagination is enabled by adding `?pagination=cursor` (or including a `?cursor` token); offset pagination is used otherwise.

---

### Schema-Driven ApiResource

[](#schema-driven-apiresource)

Extend `ApiResource` and declare a `schema()` method using the `Field`, `Relation`, `Count`, `Sum`, and `Average`schema helpers. The compiled schema drives field resolution, guard evaluation, and eager-load planning automatically.

```
use App\Models\User;
use SineMacula\ApiToolkit\Attributes\ForModel;
use SineMacula\ApiToolkit\Http\Resources\ApiResource;
use SineMacula\ApiToolkit\Schema\Count;
use SineMacula\ApiToolkit\Schema\Field;
use SineMacula\ApiToolkit\Schema\Relation;
use SineMacula\ApiToolkit\Schema\Sum;

#[ForModel(User::class)]
class UserResource extends ApiResource
{
    const RESOURCE_TYPE = 'user';

    protected static array $default = ['id', 'name', 'email', 'created_at'];

    public static function schema(): array
    {
        return Field::set(
            Field::scalar('name')->filterable()->sortable(),
            Field::scalar('email')->filterable(),
            Field::timestamp('created_at')->sortable(),
            Field::compute('full_name', 'getFullName'),
            Relation::to('organization', OrganizationResource::class)->traversable(),
            Count::of('memberships'),
            Sum::of('orders', 'total'),
        );
    }
}
```

Fields marked `filterable()` or `sortable()` are exposed under the allowlist posture. Relations marked `traversable()` can be targeted by nested filters. The `$default` static property declares the fields returned when the client sends no `?fields` parameter.

**Model binding and discovery** - the `#[ForModel(...)]` attribute binds a resource to its model, and may be repeated to bind one resource to several models. By default (`api-toolkit.resources.paths` left null) resources are discovered at boot from the application's own `Http/Resources` directory plus each module's, resolved from `app_path()` so a modular application is covered with no configuration, and compiled into the model-to-resource map automatically so no central map needs maintaining. Set `paths` to an explicit array to override the scanned roots, or an empty array to disable discovery. An explicit `api-toolkit.resources.resource_map` entry always wins over a discovered binding - use it as the canonical-resource tiebreak when a model has more than one resource, or to bind resources living outside the scanned paths. When two discovered resources claim the same model and no explicit entry resolves it, the first (in sorted file order) wins and a warning is logged; with `validate_schemas` enabled the conflict fails the boot instead.

**Eager-load planning** - the resource builds `with()`/`withCount()`/`withSum()`/`withAvg()` maps from the resolved field set, so relations are loaded precisely and automatically:

```
// Build a with() map for the active field set
$with = UserResource::eagerLoadMapFor(UserResource::resolveFields());
```

**Field-set control** at instantiation:

```
new UserResource($user, loadMissing: true);          // eager-loads missing relations
new UserResource($user, included: ['id', 'email']);   // explicit field set
new UserResource($user, excluded: ['email']);          // field set minus exclusions
(new UserResource($user))->withAll();                 // all schema fields
```

**Schema validation** - `api-toolkit.resources.validate_schemas` has all registered schemas validated during application boot. It defaults to enabled outside production (set `VALIDATE_SCHEMAS=false` to opt out) and off in production, where the boot cost is not worth paying. The `api-toolkit:validate-schemas`Artisan command runs the same validation on demand - independently of the flag - so it can also gate CI.

---

### Repositories

[](#repositories)

Extend `ApiRepository` to get a repository wired to the API query parser, eager-load planning, and pagination out of the box:

```
use SineMacula\ApiToolkit\Repositories\ApiRepository;

class UserRepository extends ApiRepository
{
    public function model(): string
    {
        return User::class;
    }
}
```

Call `withApiCriteria()` before any read to apply the parsed filters, sorts, eager loads, and limit from the current request automatically:

```
$users = $repository->withApiCriteria()->paginate();
```

**Allowlist posture** - by default (`api-toolkit.repositories.query_posture = 'allowlist'`) only schema fields declared `filterable()`, `sortable()`, or `traversable()` are accepted. Undeclared keys are rejected with a validation error (controlled by `api-toolkit.repositories.reject_undeclared`). Switch to `'blocklist'` to restore the opt-out behaviour and exclude specific columns via `api-toolkit.repositories.searchable_exclusions`.

**Cacheable trait** - add per-query transparent caching to any `ApiRepository` subclass:

```
use SineMacula\Repositories\Concerns\Cacheable;

class UserRepository extends ApiRepository
{
    use Cacheable;

    protected int $cacheTtl = 3600;
    protected ?string $cacheStoreName = null;   // uses app default
    protected bool $cacheReferenceTable = false; // whole-table reference mode
}
```

Read results are keyed by query fingerprint; write operations invalidate the table automatically. Call `withoutCache()` to bypass the cache for a single read, or `flushCache()` to invalidate immediately.

**ReferenceCache** is enabled by setting `protected bool $cacheReferenceTable = true` on a `Cacheable`repository. In reference mode the full table is loaded once and memoised in-process; single-record lookups resolve in O(1) without touching the database. Use this only for small, rarely-changing lookup tables.

**Deferrable trait** - buffer insert operations in memory and flush them as bulk `INSERT` statements at the lifecycle boundary (`RequestHandled`, `CommandFinished`, `JobProcessed`, or `JobFailed`):

```
use SineMacula\ApiToolkit\Repositories\Concerns\Deferrable;

class AuditRepository extends ApiRepository
{
    use Deferrable;
}

// In your service:
$auditRepository->defer(['user_id' => 1, 'action' => 'login']);
```

The default `on_failure = 'collect'` strategy retains failed records for the next boundary flush rather than dropping them. The `'throw'` strategy raises a `WritePoolFlushException` for callers that own an explicit flush site. The `'log'` strategy is best-effort only - use it solely for genuinely disposable writes such as telemetry.

The `WritePool` is a scoped singleton, so after a deferred write you can call `app(WritePool::class)->lastAutoFlushResult()` to observe the outcome of the most recent automatic flush (`null` if none has occurred). Under the non-throwing `'collect'` and `'log'` strategies this accessor is the only in-process signal that an auto-flush failed.

---

### Filter-Operator Registry

[](#filter-operator-registry)

The `OperatorRegistry` singleton maps token strings to `FilterOperator` handler instances. Register custom operators in a service provider's `boot()` method:

```
use SineMacula\ApiToolkit\Repositories\Criteria\OperatorRegistry;
use SineMacula\ApiToolkit\Contracts\FilterOperator;

class AppServiceProvider extends ServiceProvider
{
    public function boot(OperatorRegistry $registry): void
    {
        $registry->register('$regex', new RegexOperator);

        // Override an existing operator
        $registry->override('$like', new CaseInsensitiveLikeOperator);
    }
}
```

A `FilterOperator` is any class implementing `SineMacula\ApiToolkit\Contracts\FilterOperator`, or a closure with the same signature. Operators registered via `register()` throw `InvalidArgumentException` if the token is already taken; use `override()` to replace unconditionally.

---

### Exception Handling

[](#exception-handling)

Register the exception handler once in `bootstrap/app.php`:

```
use SineMacula\ApiToolkit\Exceptions\ApiExceptionHandler;

->withExceptions(function (Exceptions $exceptions): void {
    ApiExceptionHandler::handles($exceptions);
})
```

All exceptions are mapped to typed `ApiException` subclasses and rendered as consistent JSON error responses with appropriate HTTP status codes. The rendering strategy is configurable:

- `'auto'` (default) - renders JSON unless the request does not expect JSON and debug mode is on.
- `'always_json'` - always renders JSON.
- `'json_when_expected'` - renders JSON only when the request expects a JSON response (Laravel's `expectsJson()`, e.g. `Accept: application/json`).

**Sensitive-key redaction** - request data written to the exception log is automatically scanned and values whose keys match any substring in `api-toolkit.exceptions.sensitive_keys` (default: `password`, `token`, `secret`, `authorization`) are replaced with `[redacted]` before being written to the log. Add application-specific keys to that array in your published config to extend coverage.

---

### Middleware

[](#middleware)

The service provider registers the following middleware automatically. Each registration can be disabled independently in `api-toolkit.middleware`:

- **`maintenance_mode_swap`** - JSON `503` maintenance responses with an `except` URI allowlist.
- **`json_pretty_print`** - opt-in pretty-printed JSON responses via a query parameter.
- **`throttle`** - API-friendly rate-limit responses; auto-selects the Redis variant when Redis is the cache driver.

`json_pretty_print` accepts `'scope': 'global'` (default, pushed to the global stack) or `'scope': 'api'`(appended to the `api` middleware group only). `maintenance_mode_swap` is always prepended to the global stack when enabled, and `throttle` is registered as the router's `throttle` alias, so neither takes a scope.

Typed request capabilities (soft-delete visibility via `includeTrashed()` / `onlyTrashed()`) are available on demand through `SineMacula\ApiToolkit\Http\RequestCapabilities::fromRequest($request)`, which resolves and caches them lazily on first access - no middleware registration is required.

**Request throttling and rate-limit keying** - each request is keyed by method, host, path, and caller identity. Authenticated requests are keyed by the user identifier; guests are keyed by their client IP (`$request->ip()`), matching Laravel's stock `ThrottleRequests`. Guests are deliberately not pooled into a single shared bucket, which would let one anonymous caller exhaust the rate limit for every other guest.

Behind a shared-IP proxy, load balancer, CDN, or NAT, per-IP guest keying can over-throttle many distinct callers that share one egress IP. Configure Laravel's `TrustProxies` middleware so that `$request->ip()`resolves the real client IP rather than the proxy's.

To key guests by an application-specific identifier (for example an API key) instead of their IP, set `api-toolkit.middleware.throttle.class` to your own middleware that uses the `ThrottleRequestsTrait` and overrides `resolveRequestSignature()`. That config option is the supported customisation point.

---

### Schema Introspection and OpenAPI Export

[](#schema-introspection-and-openapi-export)

The schema compiler resolves filterable columns, sortable columns, traversable relations, and all field keys for any registered resource without instantiating it. A complementary database-schema introspector resolves model columns, searchable columns, and relations; it is used internally by `ApiCriteria` and is available for injection:

```
use SineMacula\ApiToolkit\Contracts\SchemaIntrospectionProvider;

public function __construct(private SchemaIntrospectionProvider $introspector) {}
```

An OpenAPI 3.1 components document can be generated from the registered resource map and operator grammar:

```
php artisan api-toolkit:export-openapi
php artisan api-toolkit:export-openapi --output=openapi.json
```

---

### Upgrading

[](#upgrading)

See [UPGRADE.md](UPGRADE.md) for version-by-version migration guides, including breaking changes and the steps required to move from 1.x to 2.x.

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

[](#requirements)

- PHP ^8.3
- Laravel 12+

Testing
-------

[](#testing)

```
composer test
composer test:coverage
composer check
composer format
composer smells
```

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for a list of notable changes, and [UPGRADE.md](UPGRADE.md) for version upgrade guides.

Contributing
------------

[](#contributing)

Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on branching, commits, code quality, and pull requests.

Security
--------

[](#security)

If you discover a security vulnerability, please report it responsibly. See [SECURITY.md](SECURITY.md) for the disclosure policy and contact details.

License
-------

[](#license)

Licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).

###  Health Score

54

—

FairBetter than 97% of packages

Maintenance85

Actively maintained with recent releases

Popularity30

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity71

Established project with proven stability

 Bus Factor1

Top contributor holds 93.5% 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 ~10 days

Total

58

Last Release

153d ago

Major Versions

1.x-dev → 2.x-dev2026-03-17

### Community

Maintainers

![](https://www.gravatar.com/avatar/6262ea965c244b0c946a2f29a94da05e30846c066a0b59399466216654c78fe6?d=identicon)[sinemacula](/maintainers/sinemacula)

---

Top Contributors

[![sinemacula-ben](https://avatars.githubusercontent.com/u/118753672?v=4)](https://github.com/sinemacula-ben "sinemacula-ben (418 commits)")[![sine-macula-dependencies[bot]](https://avatars.githubusercontent.com/in/4130001?v=4)](https://github.com/sine-macula-dependencies[bot] "sine-macula-dependencies[bot] (19 commits)")[![michaelstivala](https://avatars.githubusercontent.com/u/4493561?v=4)](https://github.com/michaelstivala "michaelstivala (6 commits)")[![derrickschoen](https://avatars.githubusercontent.com/u/13784912?v=4)](https://github.com/derrickschoen "derrickschoen (2 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")[![maticb](https://avatars.githubusercontent.com/u/3185053?v=4)](https://github.com/maticb "maticb (1 commits)")

---

Tags

apilaravelrestfultoolkitsine macula

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/sinemacula-laravel-api-toolkit/health.svg)

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

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k31.8M160](/packages/laravel-cashier)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

79227.1M227](/packages/laravel-mcp)[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1239.7k25](/packages/fleetbase-core-api)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45844.8k1](/packages/pressbooks-pressbooks)

PHPackages © 2026

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