PHPackages                             dandoetech/laravel-generic-api - 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. [HTTP &amp; Networking](/categories/http)
4. /
5. dandoetech/laravel-generic-api

ActiveLibrary[HTTP &amp; Networking](/categories/http)

dandoetech/laravel-generic-api
==============================

Registry-driven CRUD proxy for Laravel. Validation, policies, filtering, and consistent JSON responses.

v0.2.0(2d ago)20MITPHPPHP ^8.2CI passing

Since Mar 15Pushed 2d agoCompare

[ Source](https://github.com/dandoetech/laravel-generic-api)[ Packagist](https://packagist.org/packages/dandoetech/laravel-generic-api)[ Docs](https://github.com/dandoetech/laravel-generic-api)[ RSS](/packages/dandoetech-laravel-generic-api/feed)WikiDiscussions main Synced today

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

Laravel Generic API
===================

[](#laravel-generic-api)

> **Pre-release** — Architecture by senior tech lead, implementation largely AI-assisted with human review. Not fully reviewed. Architecture may change before v1.0.0.

Registry-driven CRUD controller for Laravel. One controller handles all resources — validation, authorization, filtering, sorting, pagination, and computed fields are derived from the Resource Registry.

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

[](#installation)

```
composer require dandoetech/laravel-generic-api
```

The service provider is auto-discovered. Publish the config:

```
php artisan vendor:publish --tag=ddt-api-config
```

Requires [`dandoetech/laravel-resource-registry`](https://github.com/dandoetech/laravel-resource-registry).

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

[](#quick-start)

Define a resource with `HasEloquentModel` (and optionally `HasPolicy`):

```
class ProductResource extends Resource implements HasEloquentModel, HasPolicy
{
    public function model(): string { return \App\Models\Product::class; }
    public function policy(): string { return \App\Policies\ProductPolicy::class; }

    protected function define(ResourceBuilder $b): void
    {
        $b->key('product')
          ->label('Product')
          ->timestamps()
          ->field('name', FieldType::String, nullable: false, rules: ['required', 'max:120'])
          ->field('price', FieldType::Float, nullable: false, rules: ['required', 'numeric', 'min:0'])
          ->field('category_id', FieldType::Integer, nullable: false)
          ->belongsTo('category', foreignKey: 'category_id')
          ->computed('category_name', FieldType::String, via: 'category.name')
          ->computed('orders_count', FieldType::Integer, via: 'count:orders')
          ->filterable(['name', 'price', 'category_id', 'category_name'])
          ->sortable(['name', 'price', 'created_at', 'orders_count'])
          ->action('create')
          ->action('update')
          ->action('delete');
    }
}
```

Routes are registered automatically. The API is ready:

```
GET    /api/v1/product                          → list (paginated)
GET    /api/v1/product?filter[name]=Phone       → filter
GET    /api/v1/product?sort=-price,name         → sort (- = desc)
GET    /api/v1/product?page=2&perPage=50        → paginate
GET    /api/v1/product/123                      → show
POST   /api/v1/product                          → create
PATCH  /api/v1/product/123                      → update
DELETE /api/v1/product/123                      → delete
POST   /api/v1/product/actions/bulk-delete      → mass action

```

Routes
------

[](#routes)

All routes are prefixed with `config('ddt_api.prefix')` (default: `api/v1`) and use the `api` middleware plus `AuthorizeResource`.

MethodRouteActionGET`{resource}`List with pagination, filtering, sortingPOST`{resource}`CreateGET`{resource}/{id}`ShowPATCH`{resource}/{id}`Update (partial)DELETE`{resource}/{id}`DeletePOST`{resource}/actions/{action}`Execute mass actionResponses
---------

[](#responses)

List:

```
{
  "data": [
    { "id": 1, "name": "Phone", "price": 599.99, "category_name": "Electronics" }
  ],
  "meta": { "page": 1, "perPage": 25, "total": 42, "lastPage": 2 }
}
```

Single item:

```
{
  "data": { "id": 1, "name": "Phone", "price": 599.99, "category_name": "Electronics" }
}
```

Query Syntax
------------

[](#query-syntax)

Only fields listed in `filterable()` and `sortable()` on the resource are accepted. Unknown fields return a `422 Unprocessable Entity`. This includes computed fields — filtering and sorting on `category_name` or `orders_count` works out of the box.

### Filtering

[](#filtering)

```
?filter[name]=Widget                           String fields: auto LIKE match
?filter[category_id]=3                         Non-string fields: exact match
?filter[price][gte]=10&filter[price][lte]=100  Operator syntax
?filter[price][between]=10,100                 Between shorthand (comma-separated)

```

**Operators:** `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `like`, `between`

### Sorting

[](#sorting)

```
?sort=name              Ascending
?sort=-price            Descending (prefix with -)
?sort=-created_at,name  Multiple fields (comma-separated)

```

### Search

[](#search)

```
?search=widget          OR LIKE across all searchable() fields

```

Search applies `LIKE %term%` for string fields and exact match for non-string fields.

### Pagination

[](#pagination)

```
?page=2&perPage=50      Default: 25, max: 200 (configurable)

```

### Query Profiles

[](#query-profiles)

Override filterable/sortable/searchable per context. Profiles can be defined on the Resource class:

```
$b->queryProfile('admin',
    filterable: ['name', 'price', 'category_id', 'created_at'],
    sortable: ['name', 'price', 'created_at', 'orders_count'],
);

// With preFilter — automatically applied WHERE conditions:
$b->queryProfile('active',
    filterable: ['name', 'price'],
    preFilter: ['status' => 'active'],
);
```

Activate with `?profile=admin` or `?profile=active`.

Authorization
-------------

[](#authorization)

The `AuthorizeResource` middleware handles authorization automatically for resources that implement `HasPolicy`:

RequestGate abilitySubjectGET (list)`viewAny`Model classGET (single)`view`Model instancePOST`create`Model classPATCH`update`Model instanceDELETE`delete`Model instanceMass action`action`Model class + action nameResources without `HasPolicy` skip authorization entirely.

Validation
----------

[](#validation)

Validation rules are derived from the resource definition:

- **POST (create):** Field rules applied directly. Non-nullable fields without explicit rules get `required`.
- **PATCH (update):** All rules wrapped with `sometimes` (PATCH semantics — only validate provided fields).

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

[](#configuration)

`config/ddt_api.php`:

```
return [
    // Route prefix for all endpoints
    'prefix' => env('GENERIC_API_PREFIX', 'api/v1'),

    // Pagination defaults
    'pagination' => [
        'per_page' => 25,
        'max_per_page' => 200,
    ],

    // Named query profiles per resource
    'query_profiles' => [
        // 'product' => [
        //     'admin' => ['filterable' => [...], 'sortable' => [...]],
        // ],
    ],

    // Mass action handlers
    'actions' => [
        // 'product' => [
        //     'bulk-delete' => App\Actions\ProductBulkDelete::class,
        // ],
    ],
];
```

Testing
-------

[](#testing)

```
composer install
composer test        # PHPUnit (Orchestra Testbench)
composer qa          # cs:check + phpstan + test
```

License
-------

[](#license)

MIT

###  Health Score

38

—

LowBetter than 86% of packages

Maintenance99

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

 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 ~4 days

Total

2

Last Release

2d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7db403e7ef98a1eb428f771172dfa0edbd6f7c72d217fad0571992bee2cc089d?d=identicon)[dandoetech](/maintainers/dandoetech)

---

Top Contributors

[![dandoetech](https://avatars.githubusercontent.com/u/5097406?v=4)](https://github.com/dandoetech "dandoetech (46 commits)")

---

Tags

phpapilaravelrestcrudgeneric-controller

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/dandoetech-laravel-generic-api/health.svg)

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

###  Alternatives

[api-platform/laravel

API Platform support for Laravel

58113.6k3](/packages/api-platform-laravel)[bjerke/laravel-bread

A boilerplate package for BREAD operations through REST API in Laravel

115.2k](/packages/bjerke-laravel-bread)

PHPackages © 2026

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