Download the PHP package devespresso/laravel-api-kit without Composer

On this page you can find all versions of the php package devespresso/laravel-api-kit. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-api-kit

Laravel API Kit

A Laravel package that provides a complete data filtering, transformation, and API response system. Drop it into any Laravel application to get automatic query filtering, model transformation, pagination, sorting, authorisation, and CRUD repositories — all driven by simple class conventions.

Requirements

Installation

Publish the config file:

Scaffolding

Generate a full API resource with a single command:

This creates all 7 components at once:

Component Generated Class
Model App\Models\Post
Repository App\Repositories\PostRepository
Controller App\Http\Controllers\PostController
Transformer App\Transformers\PostTransformer
Request App\Http\Requests\PostRequest
Authorisation App\Services\Authorisation\PostAuthorisationService
Filter Service App\Services\Filters\PostFilterService

All paths are driven by the paths config — if you customise them, the scaffold command follows automatically.

Options

Available component names for --only and --except: model, repository, controller, transformer, request, authorisation, filter-service.


Configuration

Publish the config file to config/devespressoApi.php:


pagination.with_pages

Controls the default pagination method used when no pagination_type is passed in the request.


paths

Namespaces used to auto-resolve classes and to determine where the scaffold command places generated files. Change these if your project uses a non-standard structure.


auto_select

When true, the filter service reads the active transformer format and automatically adds a SELECT clause to the query — only fetching columns that are actually needed. Prevents SELECT * without any manual effort.

Set to false to let Eloquent fall back to SELECT *, or when you need full manual control over selected columns.


auto_eager_load

When true, any relation defined as a nested array in the transformer format is automatically eager-loaded with its own scoped SELECT. Eliminates N+1 queries without writing ->with() manually.

Set to false to manage eager loading manually in your filter service or controller.


enable_explicit_filtering

When true, the filter service only dispatches request keys that are explicitly listed via the explicitFilters parameter. Keys not in the list are silently ignored. sort and search are always exempt. $autoApply is unaffected.

See Explicit Filtering for usage.


roles, numeric_roles, role_resolver

Controls role-based method restrictions in the filter service.

role_resolver must be an invokable class — closures cannot be used because the config file must be cacheable (php artisan config:cache).

See $roleMethods for usage.


transformers.prefixes

Single-character prefixes used in transformer $formats arrays to control how attributes are treated. All three are fully configurable — if any clash with your attribute names, change them here and the entire package will use your values automatically.

Key Default Effect
hidden_attributes ! Attribute is SELECTed but stripped from the response
custom_attributes @ Attribute value is resolved via $customAttributes map
accessor_attributes ~ Attribute is a Laravel model accessor — NOT added to SELECT, but included in the output via $model->attribute
unmerged_format _ Format key is not merged with the * wildcard format

Core Components

1. EnableDatabaseFiltering Trait

Add to any Eloquent model to enable filtering:

Call filter() from a controller:

filter() accepts two optional extra parameters:

Pre-scoping the query with a parent resource ($query):

Passing context into filter methods ($extras):

Using $this->user inside the filter service:

$this->user holds the authenticated user passed as the second argument to filter(). It is available anywhere in the filter service — setConditions(), filter methods, and any custom method you add to the subclass.


2. BaseFilterService

Create a filter service per model by extending BaseFilterService. Each key in the incoming request data is camelCased and dispatched to a matching method on the service.

Available helpers inside filter methods

Method Dispatch and Security

The filter service works by taking each key in the incoming request data, converting it to camelCase, and calling the matching public method on the service if it exists. For example, passing author_id=5 in the request will automatically call $this->authorId(5).

This means any public method on your filter service subclass is callable from request data by default. The package protects against this in four ways:

1. Base class methods are always blocked

All public methods defined on BaseFilterService itself (e.g. setData, setQuery, filter) are automatically guarded and can never be triggered by request data. sort and search are intentionally excluded from this list so they remain dispatchable.

2. Protected methods are automatically blocked

Only public methods can be dispatched. If you define a method as protected on your subclass, it will never be triggered by request data — no configuration needed. Use this as a natural way to write internal helper methods without worrying about accidental exposure:

3. $guardedMethods — block specific public methods on your subclass

Use this to explicitly prevent public methods on your subclass from being triggered by request data:

Any method listed here will be silently skipped even if a matching key is present in the request.

4. $roleMethods — restrict methods to specific roles

Maps role names to the methods that require them. A method is only dispatched if the current user holds that role — or a higher one:

Roles are hierarchical — a higher role automatically inherits access to all methods available to lower roles. Declare the hierarchy and a resolver in config/devespressoApi.php:

The resolver must be an invokable class — closures are not supported because the config file must be cacheable:

An admin user can trigger methods listed under admin, editor, and moderator. An editor can trigger editor and moderator methods, but not admin.

Full example:

Calling from a controller requires no extra work — role checking happens automatically:

Numeric roles — if your roles are numeric (e.g. 1, 2, 3 or 10, 20, 30), set numeric_roles to true and skip the roles list entirely. The hierarchy is derived automatically from the keys in $roleMethods:

A user with role 3 can trigger methods at levels 1, 2, and 3. A user with role 1 can only trigger level 1 methods.

That's all the setup needed — no overrides required on individual filter services.

If you need custom resolution logic for a specific service, override getEffectiveRoles():

Rule of thumb: keep internal helpers protected. If a public method should not be triggerable from a request key, add it to $guardedMethods. If it should only be available to specific roles, add it to $roleMethods.

Auto-Apply

Methods listed in $autoApply are always dispatched regardless of what is in the request data. They run after the request-driven filters and cannot be skipped by the caller:

Use this for constraints that must always be enforced — scoping to active records, filtering by tenant, etc.

Explicit Filtering

For an extra layer of security, you can restrict which request keys are allowed to drive filter methods on a per-call basis. This is controlled by two things:

  1. Config flag — enable it globally in config/devespressoApi.php:

  2. Allowed list per request — pass it through the model's filter() call:

When enable_explicit_filtering is true, the restriction always applies — there is no opt-out per call. Only keys in the allowed list are dispatched to filter methods; anything not listed is silently ignored, even if a matching public method exists. sort and search are always exempt.

Not passing an allowed list is treated as an empty list — all request-driven filters are blocked. This means every endpoint that uses filtering must explicitly declare which keys it allows:

$autoApply methods are unaffected — they always run regardless of the explicit filter list.

Pagination

Control pagination via request data:

pagination_type Result
(not set) simplePaginate() or paginate() based on config
simple simplePaginate() — no total count query
paginate paginate() — includes total count
cursor cursorPaginate() — cursor-based, no total count
none get() — returns all results

Cursor pagination and sorting

Cursor pagination works by encoding the last-seen row position into an opaque token. Laravel reads the cursor query parameter automatically — you do not need to handle it in your filter service.

Because the cursor points to a specific row, the sort order must be stable and unique across the full result set. If two rows can produce the same sort key, Laravel cannot determine a reliable position and will skip or repeat rows between pages.

Rules to follow:

The defaultSortingColumn on BaseFilterService is ['id,desc'], which is safe for cursor pagination out of the box. If you change it in your subclass, make sure to keep id as the final tiebreaker:

Laravel will throw a RuntimeException if it cannot encode a valid cursor from the current sort — which is a clear signal that the ordering is not stable enough.

Sorting

Allowed sort columns are controlled by $sortColumns. You can also define aliases via $customSortColumns:

For complex sorts that can't be expressed as a simple column — such as FIELD(), COALESCE(), or any raw SQL expression — use $rawSort to map an alias to a method on your filter service:

The method returns the raw SQL expression — no need to handle the direction. The framework appends it and calls orderByRaw() for you. Raw sort methods bypass the column allowlist entirely.

Methods listed in $rawSort are automatically guarded from request data dispatch — they cannot be triggered as filter methods regardless of their visibility.


3. BaseTransformer

Controls which model attributes are included in API responses and how they are formatted. The transformer is resolved automatically from the model name (PostTransformer for Post), or set explicitly via $transformer on the filter service or controller.

Attribute Prefixes

Prefix Meaning
!attribute Hidden — excluded from output. On a relation key, still eager-loaded for SELECT purposes but not returned.
@attribute Custom — value resolved via the $customAttributes map instead of reading from the database.
~attribute Accessor — a Laravel model accessor. Not added to the SELECT query, but read from the model and included in the output.

All prefixes are configurable via config/devespressoApi.php under transformers.prefixes. If your attribute names clash with the defaults, change them there and the entire package will use your values automatically.

Format Key Prefixes

Format key Behaviour
* Wildcard — always included, merged with the matched route key
show, index, etc. Merged on top of * for that controller method
_index Returned standalone — does not merge with *

API Versioning

When your API evolves across versions, the transformer's versioning system lets you describe what changes at each version — without creating separate transformer files.

Enable versioning in the config:

Define your base format and version methods on the transformer:

Resolution chain:

Request version Formats applied
unversioned / none baseFormat() only
v2 baseFormat()v2Format()
v3 baseFormat()v2Format()v3Format()
v445 (unknown) baseFormat()v2Format()v3Format() (falls back to latest)

Nested relations — append and remove work at any depth, mirroring the existing format shape:

Standalone versions — use merge: false to replace all accumulated formats and start fresh from that version:

Subsequent versions still build on top of the standalone result. Note that merge: false only resets the accumulated formats — property overrides (renames, formatters, guarded, defaults, customAttributes) always accumulate cumulatively regardless.

Versioned property overrides

Version methods can also override renames, formatters, guarded, defaults, and customAttributes — the same properties you set on the transformer class. The rule is simple:

What the user gets per version:

Version Active renames
base created_at → createdAt
v2 + name → fullName, author.name → authorName
v3 + status → userStatus

The chain accumulates across versions — v3's renames are merged on top of v2's, which are merged on top of the base. Later versions override earlier values for the same key.

Base properties are never mutated — calling resolveVersionedFormats() on the same transformer instance with different versions is safe. Versioned state is isolated per call and reset at the start of each resolution.

Key validation — version methods only accept the following keys. Any typo (e.g. 'appned' instead of 'append') throws an \InvalidArgumentException immediately with a clear message listing both the bad key and the valid ones:

Also, merge: false requires a formats key — omitting it throws as well.

$latestVersion — opt-in strict mode:

When set, any version within this boundary that is missing its format method throws a RuntimeException with a descriptive message. Versions beyond $latestVersion are always skipped silently — not every transformer needs to change at every version.

Driver: route_prefix — detects the version from the start of the route URI. A route registered at v2/posts/{id} resolves to v2. A route whose URI is exactly the version string (e.g. v2 with no trailing path) also matches.

Driver: header — reads the version from the configured request header:

Reading the resolved version — after setData() is called on the controller, the resolved version is available on the controller via $this->version. On the transformer it is available via getResolvedVersion(). Both return null when versioning is disabled or no version was detected.

When an unknown version is requested (e.g. v445) the system falls back to the full chain and both properties reflect the last known version (v3), not the raw requested value — so callers always see the effective version that was actually applied.


$wrapper

The $wrapper property controls the key name used to wrap the transformed data in the response. If not set, it defaults to 'data':

The wrapper can also be overridden per-call from the controller via setData($post, 'post').

Transformer-Driven Query

When auto_select and auto_eager_load are enabled, the filter service reads your transformer's $formats definition and automatically builds an optimised query — no SELECT *, no N+1.

Given this transformer:

Important: always include the foreign key that connects the relation (e.g. user_id on posts) in your transformer format. Without it, the column won't be selected and the eager-loaded relation will return empty. Use the plain key to include it in the response, or prefix it with ! to select it silently.

Calling Post::filter($request->validated(), $request->user()) generates exactly:

@word_count and ~reading_time are both excluded from SELECT — the difference is how their values are resolved: @ calls a method on the transformer, while ~ calls the model accessor directly ($model->reading_time).

And the JSON response includes only what was declared as visible — ! prefixed fields are fetched but stripped from the output:

team_id and email were selected but hidden via ! — they never appear in the response. user_id is selected and visible since it was declared without a prefix. If you changed it to '!user_id' in the transformer, it would still be selected but would disappear from the response.

The Eloquent equivalent you would otherwise write by hand:

With the package, that query is derived automatically from the transformer — you never write it, and it stays in sync with your response format as the transformer evolves.


4. BaseRepository

Provides standard CRUD with lifecycle hooks. Automatically resolves the model from the repository class name (PostRepositoryPost).

Available methods:

To skip hooks for a single operation, chain withoutHooks() before the call. The skip list resets automatically after each operation.


5. ApiController

Base controller for JSON API responses. Automatically resolves a transformer and repository from the controller class name.

Response shortcuts

respondCreated() returns a 201 Created response. respondNoContent() returns a 204 No Content response:

setRawData() — bypass the transformer

Use setRawData() to add data to the response without going through the transformer. Defaults to the 'data' key:

This is especially useful when autoResolveTransformer is disabled, or when the data doesn't come from a model.

appendTo() — accumulate multiple values under a key

Use appendTo() to push values onto a response key rather than replacing it. Each call appends to the array. Defaults to the 'data' key:

Use a custom key to keep different datasets separate:

setMeta() and addMeta() — response metadata

Attach metadata (permissions, roles, feature flags, etc.) to the response via the meta key:

setMeta() replaces the entire meta array. addMeta() adds a single key-value pair. The meta key is only included in the response when non-empty.

respond() — merging extra data

You can pass an array to respond() to merge additional data into the response, or override existing keys entirely:

setData() optional parameters

setData() accepts two optional arguments that give you finer control over the response shape:

Overriding the transformer at runtime

Use setTransformer() to swap out the auto-resolved transformer for a specific call. Useful when one controller needs to serve multiple models or formats:

setCode() and error responses

setCode() automatically sets status to "error" for any code >= 400. An optional second argument sets a custom message:

Disabling auto-resolution

Both $autoResolveRepository and $autoResolveTransformer can be set to false on the subclass to disable auto-resolution when you want full manual control:

Default response format:

meta is only present when metadata has been set via setMeta() or addMeta().


6. BaseRequest

Auto-dispatches validation rules and authorization per controller method. Includes built-in rules for pagination and sorting on all list endpoints.

Built-in rules available on all requests (from indexRules()):

Key Rule
sort string
per_page integer, min:1, max:100
with_pages boolean
pagination_type in:paginate,none,simple,cursor

7. BaseAuthorisationService

Property-based authorisation checks, usable standalone or from filter services.

Use skipExceptions() to collect errors instead of throwing:


Running Tests


License

MIT


All versions of laravel-api-kit with dependencies

PHP Build Version
Package Version
Requires php Version ^8.1
illuminate/support Version ^10.0|^11.0|^12.0|^13.0
illuminate/database Version ^10.0|^11.0|^12.0|^13.0
illuminate/http Version ^10.0|^11.0|^12.0|^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package devespresso/laravel-api-kit contains the following files

Loading the files please wait ...