Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Api Tool Kit Laravel Package

essa/api-tool-kit

Essa API Tool Kit is a Laravel package for building clean, consistent APIs faster. It provides query filtering, sorting, pagination, eager-load includes, standardized JSON responses, error handling and validation helpers, cutting boilerplate so you can focus on your endpoints.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require essa/api-tool-kit
php artisan vendor:publish --tag=api-tool-kit-config
php artisan vendor:publish --tag=api-tool-kit-lang   # tag name may differ; run `php artisan vendor:publish` and pick the package

The service provider registers via package discovery, so there is no manual providers entry in a standard Laravel app. After publishing, open config/api-tool-kit.php — this is the file that decides your response envelope keys, default status codes, media disks, and localization behaviour. Everything else in the package reads from it, so read it before you write a single controller.

Where to look first

  1. README.md → "Quick start" / usage section. This package is documentation-led; the source is small but the intended usage is spelled out there.
  2. config/api-tool-kit.php → the contract for your whole API surface.
  3. src/Http/Controllers/ → the base ApiController (and any resource-controller base).
  4. src/Http/Requests/ → the base ApiRequest that shapes validation failures.
  5. src/Traits/ and src/Models/ → media, filtering, enum and response traits you will mix into your own classes.

First use case — one read endpoint, one write endpoint

Build GET /api/v1/posts and POST /api/v1/posts end to end before touching anything else. That single pair exercises the three things the package actually buys you: a uniform success envelope, a uniform validation-failure envelope, and a consistent error handler.

// routes/api.php
Route::apiResource('v1/posts', PostController::class);

// app/Http/Controllers/Api/PostController.php
class PostController extends ApiController
{
    public function index() {
        return $this->successResponse(PostResource::collection(Post::paginate()));
    }

    public function store(StorePostRequest $request) {
        $post = Post::create($request->validated());
        return $this->successResponse(PostResource::make($post), 201);
    }
}

Verify the envelope against the config, not against the README — the README may target a different major version than the one you installed.

Implementation Patterns

Base-class inheritance as the primary pattern

The package is designed around inheritance, not composition. You extend ApiController, you extend ApiRequest, and optionally you extend the resource/model bases. Adopt this deliberately: create your own thin App\Http\Controllers\Api\ApiController extends Essa\ApiToolKit\...\ApiController and have every controller extend yours. This costs five minutes and gives you one place to change when the package's response shape shifts.

Uniform response helpers

Never return raw arrays or bare response()->json() from an API controller. Route everything through the success/error helpers so status codes, envelope keys, and message keys stay in lockstep. For errors, prefer the package's error helper with a translation key over inline English strings — it makes the localization middleware actually useful.

Form requests for the 422 contract

Extending the package's ApiRequest is what makes invalid input return your API envelope instead of Laravel's default HTML redirect or bare {message, errors}. If you have existing requests extending Illuminate\Foundation\Http\FormRequest, either migrate the hot endpoints first or add a shared base class and change the extends — mixed request bases are the most common source of "why is this one endpoint returning a different error shape".

Filtering, sorting and includes as an allowlist

If the package ships a ModelFilter base (check src/ for the filter namespace), the day-to-day pattern is: define $allowedFilters, $allowedSorts, $allowedIncludes on each filter class, and let the query string drive the rest. Treat these arrays as a security boundary, not a convenience list — see Gotchas.

Media / file uploads

The media trait pattern is: add the trait + interface to the model, define collections, then accept the upload in the request and attach it. Keep disk configuration in config/api-tool-kit.php rather than in the controller so test environments can swap to a fake disk.

Exception handling as a single choke point

Register the package's exception renderer (usually via the config or a withExceptions callback in bootstrap/app.php on Laravel 11+). Then delete or narrow the custom render methods you already have. Two competing renderers is a debugging nightmare, and the symptom — one endpoint returning HTML or a stack trace in a JSON API — is confusing until you find the second handler.

Versioning + testing

Since the package standardises the envelope, write one assertJsonStructure helper in your test base class and reuse it across every endpoint test. That helper is your regression detector when you upgrade the package.

Gotchas and Tips

Envelope double-wrapping. Laravel's JsonResource already wraps in data. If the package's successResponse also adds a data key, you get {data: {data: [...]}}. Fix it by calling ->withoutWrapping() on the resource or by passing the resource's resolve() — pick one convention and enforce it, because a per-endpoint mix is nearly impossible to clean up later.

Paginated collections lose meta/links. Resource::collection($paginator) behaves differently from Resource::collection($paginator->items()). If your pagination metadata vanishes after switching to the response helper, this is why. Check how the helper unwraps a ResourceCollection in src/.

Validation errors only look right with the right request base. A 422 that returns HTML, or {"message":"...","errors":{...}} without your envelope, almost always means the request does not extend the package's ApiRequest. Second most likely cause: the client isn't sending Accept: application/json, so Laravel's default handler kicks in regardless.

Config caching bites on upgrade. php artisan config:cache and php artisan route:cache in production will happily serve a stale package config after a composer update. Always php artisan optimize:clear post-deploy, and add a config-cache step to CI so a bad config fails there rather than in prod.

Filter and sort parameters are user input. Anything mapped from ?sort= or ?filter[col]= straight into orderBy/where is an injection and enumeration surface until proven otherwise. Keep the allowlists exhaustive, and never fall back to the raw query string when a key isn't whitelisted — silently ignore instead.

include parameters cause N+1 storms. Even with an allowlist, deep nesting (?include=posts.comments.author) explodes query counts. Cap the depth and pair the feature with Model::preventLazyLoading() in non-production.

Media cleanup is on you. Files attached to models aren't removed on model delete or failed validation unless the trait's deleting hook covers it. Add an observer or a scheduled prune; also run storage:link and verify the disk in config matches where your web server actually serves from.

Localization is middleware-dependent. Translated messages won't switch unless the locale middleware is registered and the client's Accept-Language header (or a ?lang= convention) is honoured. Publish the lang files before you translate anything, or your keys resolve to the key string.

Other packages fight for the exception handler. Sanctum's 401, Telescope's debug views, and any existing render() in your Handler can override or be overridden by the package's renderer. Confirm precedence with a deliberate abort(401) test rather than assuming.

Maturity and supply-chain caution — this is the real risk. 532 stars but zero dependents, and a last-release timestamp that reads as future-dated relative to normal registry data. That combination is not a red flag on code quality, but it does mean: pin an exact version in composer.json, read the changelog between bumps, and treat the package as an implementation detail rather than the backbone of your architecture. Wrap the response helpers, request base, and filter base in your own abstract classes so that replacing the package is a mechanical change rather than a rewrite. If it goes unmaintained, you keep the behaviour and drop the dependency.

Debugging tips. Log the resolved payload (Log::debug(json_encode($response->getData()))) at the controller boundary when a client reports a shape mismatch — it separates "package changed the envelope" from "client misparses". For filter bugs, ->toSql() on the filter-built query is faster than reading the filter source. For exception bugs, dump(app()->make(Illuminate\Contracts\Debug\ExceptionHandler::class)) tells you which handler is actually bound.

Extension points to learn once. The published config (behaviour), the published lang files (messages), the base classes (behaviour + shape), and any publishable stubs (generators). Between those four you can customise nearly everything without forking — and you should exhaust them before forking, because a fork with zero upstream dependents gets no security patches from anywhere.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package