PHPackages                             anil/fast-api-crud - 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. anil/fast-api-crud

ActiveLibrary[API Development](/categories/api)

anil/fast-api-crud
==================

A powerful Laravel package for building RESTful API CRUD operations with minimal boilerplate. Features pagination, filtering, sorting, soft deletes, permissions, lifecycle hooks, and more.

v2.0.4.12(2y ago)97.7k↑426.7%8[2 PRs](https://github.com/anilkumarthakur60/fast-api-crud/pulls)MITPHPCI failing

Since Apr 10Pushed 3d ago1 watchersCompare

[ Source](https://github.com/anilkumarthakur60/fast-api-crud)[ Packagist](https://packagist.org/packages/anil/fast-api-crud)[ RSS](/packages/anil-fast-api-crud/feed)WikiDiscussions 3.x Synced yesterday

READMEChangelog (10)Dependencies (8)Versions (30)Used By (0)

Fast API CRUD for Laravel
=========================

[](#fast-api-crud-for-laravel)

A powerful Laravel package that provides full-featured CRUD operations with minimal boilerplate. Works for both **API** (JSON responses) and **Web** (Blade views) controllers out of the box.

Supports pagination (length-aware, simple, cursor), filtering, sorting, search, soft deletes, Spatie permissions, lifecycle hooks, and much more.

**Supports:** Laravel 11, 12, 13 | PHP 8.2+

**Requires:** [spatie/laravel-permission](https://github.com/spatie/laravel-permission) ^6.0 or ^7.0

---

Table of Contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [API Controller](#api-controller-basecontroller)
- [Web Controller](#web-controller-basewebcontroller)
- [Scaffolding Command](#scaffolding-command)
- [Query Parameters](#query-parameters)
- [Controller Properties](#controller-properties)
- [Lifecycle Hooks](#lifecycle-hooks)
- [Contracts](#contracts)
- [Model Traits](#model-traits)
- [Builder Macros](#builder-macros)
- [Collection Macros](#collection-macros)
- [API Responder](#api-responder)
- [Helper Functions](#helper-functions)
- [Exceptions](#exceptions)
- [Permissions](#permissions)
- [Routes](#routes)
- [Pagination Utility](#pagination-utility)
- [Enums](#enums)
- [Full Example](#full-example)

---

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

[](#installation)

```
composer require anil/fast-api-crud
```

Publish the config file (optional):

```
php artisan vendor:publish --provider="Anil\FastApiCrud\FastApiCrudServiceProvider" --tag=config
```

---

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

[](#quick-start)

### 1. Generate everything with one command

[](#1-generate-everything-with-one-command)

```
php artisan fast-api:make-all Post
```

This creates: Model, Migration, Factory, Seeder, Controller, Resource, Store/Update Requests.

### 2. The generated controller — zero boilerplate

[](#2-the-generated-controller--zero-boilerplate)

```
// app/Http/Controllers/PostController.php

class PostController extends BaseController
{
    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}
```

### 3. Register routes

[](#3-register-routes)

One line registers the entire CRUD route set via the `fastApiResource` macro:

```
// routes/api.php

use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

Route::fastApiResource('posts', PostController::class);

// Partial registration, mirroring Laravel's apiResource:
Route::fastApiResource('posts', PostController::class, ['only' => ['index', 'show']]);
Route::fastApiResource('posts', PostController::class, ['except' => ['delete', 'restoreAll']]);
```

This registers:

```
GET     /posts                         index
POST    /posts                         store
DELETE  /posts                         delete           (bulk; delete_rows[])
POST    /posts/restore                 restoreAll
GET     /posts/{id}                    show
PUT|PATCH /posts/{id}                  update
DELETE  /posts/{id}                    destroy
PATCH   /posts/{id}/status             changeStatus
PATCH   /posts/{id}/status/{column}    updateColumn
PATCH   /posts/{id}/restore            restore
DELETE  /posts/{id}/force              permanentDelete

```

Prefer to wire routes by hand? That works too:

```
Route::get('posts', [PostController::class, 'index']);
Route::post('posts', [PostController::class, 'store']);
Route::get('posts/{id}', [PostController::class, 'show']);
Route::put('posts/{id}', [PostController::class, 'update']);
Route::delete('posts/{id}', [PostController::class, 'destroy']);
```

### 4. That's it! You now have a fully working CRUD API

[](#4-thats-it-you-now-have-a-fully-working-crud-api)

```
GET    /posts              → Paginated list with filtering, sorting, search
GET    /posts/1            → Single resource
POST   /posts              → Create (validates via StorePostRequest)
PUT    /posts/1            → Update (validates via UpdatePostRequest)
DELETE /posts/1            → Soft delete (or force delete)

```

---

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

[](#configuration)

File: `config/fast-api.php`

```
return [
    // Pagination
    'pagination' => [
        'default_per_page' => 15,    // Default records per page
        'max_per_page'     => 100,   // Maximum allowed per page
        'allow_all'        => true,  // Allow rowsPerPage=0 to fetch all
    ],

    // Bulk delete
    'bulk' => [
        'max_rows' => 1000,          // Max IDs per bulk delete (0 = unlimited)
        'field'    => 'delete_rows', // Request key holding the array of IDs
    ],

    // Query parameter keys — rename any of these to match your API conventions.
    // Defaults reproduce: ?filters={...}&sortBy=...&descending=...&rowsPerPage=...&page=...&search=...
    'query' => [
        'filters'    => 'filters',
        'search'     => 'search',
        'sort_by'    => 'sortBy',
        'descending' => 'descending',
        'per_page'   => 'rowsPerPage',
        'page'       => 'page',
        'cursor'     => 'cursor',
        'include'    => 'include',   // key read from inside the filters JSON
        'trashed'    => 'trashed',   // key read from inside the filters JSON
    ],

    // Default sort when the request has no sort key (and the model isn't Sortable)
    'sorting' => [
        'default_column'     => 'id',
        'default_descending' => true,
    ],

    // Soft Delete
    'soft_delete' => [
        'anonymize_unique_columns' => true,  // Append _{timestamp} to unique columns on delete
    ],

    // Response envelope keys
    'response' => [
        'success_key' => 'data',
        'error_key'   => 'errors',
        'message_key' => 'message',
    ],

    // Spatie permissions
    'permissions' => [
        'enabled' => true,
    ],

    // Web (Blade) flash message keys
    'web' => [
        'flash_key_success' => 'success',
        'flash_key_error'   => 'error',
    ],
];
```

### Rename query parameters

[](#rename-query-parameters)

Every request key the index reads is configurable, so you can shape the public API your way. For example, to use `?limit=`, `?sort=`, `?dir=`, `?q=`:

```
'query' => [
    'per_page'   => 'limit',
    'sort_by'    => 'sort',
    'descending' => 'dir',
    'search'     => 'q',
],
```

```
GET /posts?q=laravel&sort=created_at&dir=true&limit=25

```

---

API Controller (BaseController)
-------------------------------

[](#api-controller-basecontroller)

For JSON API endpoints. Returns `JsonResponse`, `JsonResource`, and `AnonymousResourceCollection`.

### Constructor

[](#constructor)

```
use Anil\FastApiCrud\Http\Controllers\BaseController;

class PostController extends BaseController
{
    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}
```

### Available Methods

[](#available-methods)

MethodHTTPReturn TypeDescription`index()`GET`AnonymousResourceCollection`Paginated list`show($id)`GET`JsonResource`Single resource`store()`POST`JsonResponse` (201)Create resource`update($id)`PUT`JsonResource|JsonResponse`Update resource`destroy($id)`DELETE`JsonResponse` (204)Delete resource`delete()`POST`JsonResponse` (204)Bulk delete via `delete_rows` array`changeStatus($id, $column = 'status')`PUT`JsonResource|JsonResponse`Toggle boolean column (0/1)`updateColumn($id, $column = 'status')`PUT`JsonResource|JsonResponse`Update specific fillable column`restore($id)`PUT`JsonResource|JsonResponse`Restore soft-deleted`restoreAll()`POST`JsonResponse` (204)Restore all trashed`permanentDelete($id)`POST`JsonResponse` (204)Force delete trashedMethods returning `JsonResource|JsonResponse` return `JsonResponse` when an exception occurs during the operation.

### Example Responses

[](#example-responses)

**GET /posts** (index):

```
{
  "data": [
    {
      "id": 1,
      "name": "First Post",
      "status": 1,
      "created_at": "2025-01-15T10:30:00.000000Z"
    },
    {
      "id": 2,
      "name": "Second Post",
      "status": 1,
      "created_at": "2025-01-16T14:00:00.000000Z"
    }
  ],
  "links": {
    "first": "http://example.com/posts?page=1",
    "last": "http://example.com/posts?page=5",
    "prev": null,
    "next": "http://example.com/posts?page=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 5,
    "per_page": 15,
    "to": 15,
    "total": 68
  }
}
```

**GET /posts/1** (show):

```
{
  "data": {
    "id": 1,
    "name": "First Post",
    "desc": "Post description",
    "status": 1,
    "active": 1,
    "created_at": "2025-01-15T10:30:00.000000Z",
    "updated_at": "2025-01-15T10:30:00.000000Z"
  }
}
```

**POST /posts** (store — 201 Created):

```
{
  "data": {
    "id": 3,
    "name": "New Post",
    "desc": "Description",
    "status": 1,
    "active": 1,
    "created_at": "2025-01-17T09:00:00.000000Z",
    "updated_at": "2025-01-17T09:00:00.000000Z"
  }
}
```

**DELETE /posts/1** (destroy — 204):

```
{
  "data": []
}
```

**Error response** (400):

```
{
  "errors": [],
  "message": "Something went wrong"
}
```

**Validation error** (422 — handled by Laravel):

```
{
  "message": "The name field is required.",
  "errors": {
    "name": ["The name field is required."]
  }
}
```

---

Web Controller (BaseWebController)
----------------------------------

[](#web-controller-basewebcontroller)

For Blade/web applications. Returns `View` and `RedirectResponse` with flash messages.

### Constructor

[](#constructor-1)

```
use Anil\FastApiCrud\Http\Controllers\BaseWebController;

class PostController extends BaseWebController
{
    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            viewPrefix: 'admin.posts',     // views: admin.posts.index, admin.posts.create, etc.
            routePrefix: 'admin.posts',    // redirects: route('admin.posts.index')
            resourceName: 'post',          // $post variable in views
            collectionName: 'posts',       // $posts variable in index view
            resource: PostResource::class, // Optional — for data transformation before views
        );
    }
}
```

### Available Methods

[](#available-methods-1)

MethodHTTPReturnDescription`index()`GET`View`List page (`{viewPrefix}.index`)`create()`GET`View`Create form (`{viewPrefix}.create`)`store()`POST`RedirectResponse`Create, redirect with flash`show($id)`GET`View`Detail page (`{viewPrefix}.show`)`edit($id)`GET`View`Edit form (`{viewPrefix}.edit`)`update($id)`PUT`RedirectResponse`Update, redirect with flash`destroy($id)`DELETE`RedirectResponse`Delete, redirect with flash`delete()`POST`RedirectResponse`Bulk delete, redirect`changeStatus($id)`PUT`RedirectResponse`Toggle status, redirect`restore($id)`PUT`RedirectResponse`Restore, redirect`restoreAll()`POST`RedirectResponse`Restore all, redirect`permanentDelete($id)`POST`RedirectResponse`Force delete, redirect### View Variables

[](#view-variables)

- **index**: `$posts` (or your `$collectionName`) — paginated collection
- **show/edit**: `$post` (or your `$resourceName`) — single model instance

### Customizing Flash Messages

[](#customizing-flash-messages)

Override any of the message methods in your controller. All return `string` and work with Laravel's `__()` translation helper:

```
class PostController extends BaseWebController
{
    protected function storeSuccessMessage(): string       { return __('Post created!'); }
    protected function updateSuccessMessage(): string      { return __('Post updated!'); }
    protected function destroySuccessMessage(): string     { return __('Post deleted.'); }
    protected function bulkDeleteSuccessMessage(): string  { return __('Posts deleted.'); }
    protected function statusChangeSuccessMessage(): string    { return __('Status updated.'); }
    protected function columnUpdateSuccessMessage(): string    { return __('Column updated.'); }
    protected function restoreSuccessMessage(): string         { return __('Post restored.'); }
    protected function restoreAllSuccessMessage(): string      { return __('All posts restored.'); }
    protected function permanentDeleteSuccessMessage(): string { return __('Post permanently deleted.'); }
}
```

Flash messages use session keys from config (`fast-api.web.flash_key_success` and `fast-api.web.flash_key_error`).

### Overriding Views

[](#overriding-views)

Override any method to pass extra data to views:

```
class PostController extends BaseWebController
{
    public function create(): \Illuminate\View\View
    {
        return view($this->viewName('create'), [
            'categories' => Category::all(),
            'tags' => Tag::all(),
        ]);
    }

    public function edit(int|string $id): \Illuminate\View\View
    {
        $query = $this->buildShowQuery();

        return view($this->viewName('edit'), [
            $this->resourceName => $query->findOrFail($id),
            'categories' => Category::all(),
        ]);
    }
}
```

### Blade View Example

[](#blade-view-example)

```
{{-- resources/views/admin/posts/index.blade.php --}}
@extends('layouts.app')

@section('content')

    @if(session('success'))
        {{ session('success') }}
    @endif

    @if(session('error'))
        {{ session('error') }}
    @endif

    Create Post

                ID
                Name
                Actions

            @foreach($posts as $post)

                    {{ $post->id }}
                    {{ $post->name }}

                        View
                        Edit

                            @csrf
                            @method('DELETE')
                            Delete

            @endforeach

    {{ $posts->links() }}

@endsection
```

### Web Routes

[](#web-routes)

```
// routes/web.php

use App\Http\Controllers\PostController;

Route::resource('admin/posts', PostController::class);

// Additional routes for extended operations
Route::put('admin/posts/{id}/status-change', [PostController::class, 'changeStatus']);
Route::put('admin/posts/{id}/restore', [PostController::class, 'restore']);
Route::post('admin/posts/restore-all', [PostController::class, 'restoreAll']);
Route::post('admin/posts/{id}/permanent-delete', [PostController::class, 'permanentDelete']);
```

---

Scaffolding Command
-------------------

[](#scaffolding-command)

```
# Generate API controller + resources
php artisan fast-api:make-all Post

# Generate Web controller + Blade views
php artisan fast-api:make-all Post --web

# Multiple models at once
php artisan fast-api:make-all Post,Tag,Category

# Multiple models with web
php artisan fast-api:make-all Post,Tag,Category --web
```

**Generated files per model:**

FilePathModel`app/Models/Post.php`Migration`database/migrations/create_posts_table.php`Factory`database/factories/PostFactory.php`Seeder`database/seeders/PostSeeder.php`Resource`app/Http/Resources/Post/PostResource.php`Store Request`app/Http/Requests/Post/StorePostRequest.php`Update Request`app/Http/Requests/Post/UpdatePostRequest.php`Controller`app/Http/Controllers/PostController.php`Views (--web)`resources/views/posts/index.blade.php`, `create.blade.php`, `edit.blade.php`, `show.blade.php`---

Query Parameters
----------------

[](#query-parameters)

The package reads these query parameters automatically:

### Filtering

[](#filtering)

```
GET /posts?filters={"active":1,"status":1,"queryFilter":"search term"}

```

The `filters` parameter accepts a JSON object. Each key is matched against the model's scopes:

- `active` calls `scopeActive(1)` on the model
- `queryFilter` calls `scopeQueryFilter("search term")`

### Sorting

[](#sorting)

```
GET /posts?sortBy=created_at&descending=true

```

- `sortBy` — column name to sort by (default: `id`)
- `descending` — `true` for DESC, `false` for ASC (default: `true`)

If the model implements `Sortable`, its defaults are used when no sort params are provided.

### Pagination

[](#pagination)

```
GET /posts?rowsPerPage=25
GET /posts?rowsPerPage=0     # Returns all records (if allow_all is true)

```

- `rowsPerPage` — records per page (default: 15, max: 100)

### Search

[](#search)

```
GET /posts?search=laravel

```

If the model implements the `Searchable` interface, performs a LIKE search across the columns returned by `searchableColumns()`.

### Includes (client-driven eager loading)

[](#includes-client-driven-eager-loading)

Passed as an `include` key **inside the filters JSON** (string or array form):

```
GET /posts?filters={"include":"user,tags"}
GET /posts?filters={"include":["user","tags"]}

```

Eager loads relations on demand. Only relations listed in the controller's `$allowedIncludes` allowlist are honoured — anything else is silently ignored. Works on both index and show.

### Trashed (soft-deleted records)

[](#trashed-soft-deleted-records)

Passed as a `trashed` key inside the filters JSON:

```
GET /posts?filters={"trashed":"with"}    # include soft-deleted alongside active
GET /posts?filters={"trashed":"only"}    # only soft-deleted

```

Opt-in: set `$allowTrashedFilter = true` on the controller. Ignored unless the model uses `SoftDeletes`.

### Combined Example

[](#combined-example)

One `filters` param drives scopes, includes, and trashed together:

```
GET /posts?filters={"active":1,"queryFilter":"laravel","include":"user,tags","trashed":"with"}&sortBy=name&descending=false&rowsPerPage=20&search=eloquent

```

---

Controller Properties
---------------------

[](#controller-properties)

Customize behavior by setting properties in your controller:

```
class PostController extends BaseController
{
    // Pagination
    protected PaginationType $paginationType = PaginationType::LengthAware;
    // Options: LengthAware, Simple, Cursor, None

    // Index query customization
    protected array $scopes = ['active'];                    // Apply scopes to index
    protected array $with = ['user', 'tags'];                // Eager load in index
    protected array $withCount = ['comments'];               // Count relations in index
    protected array $withAggregate = ['ratings' => 'score']; // Aggregates in index

    // Show query customization
    protected array $loadScopes = [];                        // Scopes for show
    protected array $load = ['user', 'tags', 'comments'];   // Eager load in show
    protected array $loadCount = ['comments'];               // Count in show
    protected array $loadAggregate = [];                     // Aggregates in show

    // Client-driven query features (driven via the filters JSON)
    protected array $allowedIncludes = ['user', 'tags'];     // filters={"include":"user,tags"}
    protected bool $allowTrashedFilter = false;              // filters={"trashed":"with"|"only"}

    // Operation scopes
    protected array $deleteScopes = [];     // Scopes when finding record for delete
    protected array $updateScopes = [];     // Scopes when finding record for update
    protected array $columnScopes = [];     // Scopes for changeStatus/updateColumn
    protected array $restoreScopes = [];    // Scopes for restore operations

    // Delete behavior
    protected bool $forceDelete = false;    // true = permanent delete, false = soft delete

    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}
```

### Scopes with Parameters

[](#scopes-with-parameters)

```
// Simple scopes (no parameters)
protected array $scopes = ['active', 'published'];

// Parameterized scopes
protected array $scopes = [
    'status' => 1,
    'active' => 1,
    'type'   => 'article',
];

// Array parameters
protected array $scopes = [
    'statusIn' => [1, 2, 3],
];

// Closure parameters
protected array $scopes = [
    'custom' => function ($query) {
        $query->where('featured', true);
    },
];

// Mixed
protected array $scopes = [
    'active',
    'status' => 1,
];
```

All scope properties (`$scopes`, `$loadScopes`, `$deleteScopes`, `$updateScopes`, `$columnScopes`, `$restoreScopes`) support the same syntax.

---

Lifecycle Hooks
---------------

[](#lifecycle-hooks)

Define methods on your **model** to hook into CRUD operations. These are called automatically by the controller.

```
class Post extends Model
{
    // Called before/after store()
    public function beforeCreate(): void
    {
        $this->slug = Str::slug($this->name);
    }

    public function afterCreate(): void
    {
        // Sync relations from request
        if (request()->filled('tag_ids')) {
            $this->tags()->sync(request()->input('tag_ids'));
        }
    }

    // Called before/after update()
    public function beforeUpdate(): void { }
    public function afterUpdate(): void { }

    // Called before/after destroy() and delete()
    public function beforeDelete(): void { }
    public function afterDelete(): void { }

    // Called before/after changeStatus()
    public function beforeStatusChange(): void { }
    public function afterStatusChange(): void { }

    // Called before/after updateColumn()
    public function beforeColumnUpdate(): void { }
    public function afterColumnUpdate(): void { }

    // Called before/after restore()
    public function beforeRestore(): void { }
    public function afterRestore(): void { }

    // Called before/after permanentDelete()
    public function beforeForceDelete(): void { }
    public function afterForceDelete(): void { }
}
```

You can also override hooks in the **controller**:

```
class PostController extends BaseController
{
    protected function afterCreate(Model $model): void
    {
        // Controller-level hook overrides model hook
        Notification::send($admins, new PostCreated($model));
    }
}
```

---

Contracts
---------

[](#contracts)

### Searchable

[](#searchable)

Enables automatic LIKE search on `?search=` query parameter.

```
use Anil\FastApiCrud\Contracts\Searchable;

class Post extends Model implements Searchable
{
    public function searchableColumns(): array
    {
        return [
            'name',
            'desc',
            'user:name,email',  // Search in related model columns
        ];
    }
}
```

**Request:** `GET /posts?search=laravel`

Generates: `WHERE (name LIKE '%laravel%' OR desc LIKE '%laravel%' OR EXISTS (SELECT ... FROM users WHERE name LIKE '%laravel%' OR email LIKE '%laravel%'))`

### Sortable

[](#sortable)

Provides default sort configuration when no `sortBy` query parameter is given.

```
use Anil\FastApiCrud\Contracts\Sortable;

class Post extends Model implements Sortable
{
    public function sortByDefaults(): array
    {
        return [
            'sortBy'     => 'created_at',
            'sortByDesc' => true,
        ];
    }
}
```

### HasPermissionSlug

[](#haspermissionslug)

Provides a permission slug for use with `permissionMiddleware()` in controllers.

```
use Anil\FastApiCrud\Contracts\HasPermissionSlug;

class Post extends Model implements HasPermissionSlug
{
    public function getPermissionSlug(): string
    {
        return 'posts';
    }
}
```

Use the slug in your controller's `middleware()` method (see [Permissions](#permissions)):

ActionPermissionRoutesView`view-posts`index, showStore`store-posts`storeUpdate`update-posts`update, updateColumnDelete`delete-posts`destroy, delete, permanentDeleteChange Status`change-status-posts`changeStatusRestore`restore-posts`restore, restoreAll---

Model Traits
------------

[](#model-traits)

### HasDateScopes

[](#hasdatescopes)

Adds query scopes for common date ranges. All accept an optional `$column` parameter (default: `created_at`).

```
use Anil\FastApiCrud\Concerns\HasDateScopes;

class Post extends Model
{
    use HasDateScopes;
}
```

**Available scopes:**

```
Post::query()->today();                // Records from today
Post::query()->yesterday();            // Records from yesterday
Post::query()->thisWeek();             // Monday to now
Post::query()->lastWeek();             // Last Monday to Sunday
Post::query()->monthToDate();          // 1st of month to now
Post::query()->thisMonth();            // Entire current month
Post::query()->lastMonth();            // Entire previous month
Post::query()->quarterToDate();        // Start of quarter to now
Post::query()->lastQuarter();          // Previous quarter
Post::query()->yearToDate();           // January 1 to now
Post::query()->lastYear();             // Last 12 months
Post::query()->last7Days();            // Last 7 days
Post::query()->last30Days();           // Last 30 days
Post::query()->date('2025-01-01 to 2025-01-31');  // Custom range

// Use a different column
Post::query()->today('published_at');
Post::query()->lastMonth('updated_at');
```

### UUID primary keys

[](#uuid-primary-keys)

This package does not ship a UUID trait — use Laravel's first-party traits, which set `incrementing`/`keyType`, fill the key on creation, and add UUID-aware route model binding:

```
use Illuminate\Database\Eloquent\Concerns\HasUuids;          // UUID v7 (time-ordered, recommended)
// use Illuminate\Database\Eloquent\Concerns\HasVersion4Uuids; // ordered UUID

class Post extends Model
{
    use HasUuids;
}
```

Ordered UUIDs (v7) are recommended for primary keys because of their B-tree index locality. Reach for a pure-random v4 only if you must hide record creation order.

### AnonymizesOnDelete

[](#anonymizesondelete)

Anonymizes unique column values on soft delete to prevent constraint violations.

```
use Anil\FastApiCrud\Concerns\AnonymizesOnDelete;

class User extends Model
{
    use SoftDeletes, AnonymizesOnDelete;
}
```

When soft-deleted, unique columns get `_{timestamp}` appended:

```
email: john@example.com → john@example.com_1705312800

```

This prevents conflicts when creating a new user with `john@example.com` while the old record is soft-deleted. Controlled by `fast-api.soft_delete.anonymize_unique_columns` config.

### ReplicatesWithRelations

[](#replicateswithrelations)

Replicate a model along with all its loaded relations.

```
use Anil\FastApiCrud\Concerns\ReplicatesWithRelations;

class Post extends Model
{
    use ReplicatesWithRelations;
}

// Usage
$post = Post::with(['tags', 'comments', 'author'])->find(1);
$clone = $post->replicateWithRelations();
// $clone is a saved copy with all relations duplicated
```

**Supported relations:** BelongsTo, MorphTo, HasOne, MorphOne, HasMany, MorphMany, BelongsToMany, MorphToMany

**Not supported:** HasOneThrough and HasManyThrough relations will throw an `Exception` during replication.

The trait also re-applies castable attributes (numeric, boolean, string, json) to the replicated model to ensure proper type handling.

---

Builder Macros
--------------

[](#builder-macros)

These macros are registered on `Illuminate\Database\Eloquent\Builder` and available on all queries.

### initializer

[](#initializer)

```
->initializer(bool $orderBy = true): Builder
```

Apply request-based filters, sorting, and scopes automatically.

```
$query = Post::query()->initializer();

// With orderBy disabled
$query = Post::query()->initializer(orderBy: false);
```

**How it works:**

1. Reads `?filters={"scope":"value"}` — decodes JSON, calls matching model scopes (uses `Str::studly` to find `scope{Name}` methods)
2. Reads `?sortBy=column&descending=true` — applies ordering
3. If model implements `Sortable` and no sort params given, uses `sortByDefaults()`
4. Default sort: `id` descending

### likeWhere

[](#likewhere)

```
->likeWhere(array $attributes, ?string $searchTerm = null): Builder
```

Multi-column LIKE search with relation support. Returns the query unmodified if `$searchTerm` is null or empty.

```
// Simple columns
Post::query()->likeWhere(['name', 'desc'], 'laravel');

// With relation columns (colon syntax: 'relation:column1,column2')
Post::query()->likeWhere(['name', 'user:name,email', 'tags:name'], 'search');

// Null search returns query unchanged
Post::query()->likeWhere(['name'], null);  // No-op
```

### paginates

[](#paginates)

```
->paginates(array $columns = ['*'], string $pageName = 'page', ?int $page = null): Paginator
```

Length-aware pagination using `rowsPerPage` request parameter.

```
Post::query()->paginates();                              // Default columns
Post::query()->paginates(['id', 'name']);                 // Specific columns
Post::query()->paginates(['*'], 'p', 2);                 // Custom page name & page
```

- Respects `fast-api.pagination.max_per_page` (default: 100)
- When `rowsPerPage=0` and `fast-api.pagination.allow_all=true`, returns all records

### simplePaginates

[](#simplepaginates)

```
->simplePaginates(array $columns = ['*'], string $pageName = 'page', ?int $page = null): Paginator
```

Simple pagination (no total count) using `rowsPerPage` request parameter. Same behavior as `paginates()` but without total count query.

### cursorPaginates

[](#cursorpaginates)

```
->cursorPaginates(array $columns = ['*'], ?string $cursorName = null, ?Cursor $cursor = null): CursorPaginator
```

Cursor-based pagination using `rowsPerPage` request parameter. Best for infinite scroll or large datasets.

### withAggregates

[](#withaggregates)

```
->withAggregates(array $aggregates): Builder
```

Apply multiple aggregate functions in a single call.

```
Post::query()->withAggregates([
    'comments' => 'id',                    // withAggregate('comments', 'id')
    'ratings'  => ['score', 'avg'],        // withAggregate('ratings', 'score', 'avg')
]);
```

### withCountWhereHas / orWithCountWhereHas

[](#withcountwherehas--orwithcountwherehas)

```
->withCountWhereHas(string $relation, ?Closure $callback = null, string $operator = '>=', int $count = 1): Builder
->orWithCountWhereHas(string $relation, ?Closure $callback = null, string $operator = '>=', int $count = 1): Builder
```

Adds a conditional `withCount` that also filters results using `whereHas` (or `orWhereHas`).

```
// Basic — posts that have at least 1 comment, with comment count
Post::query()->withCountWhereHas('comments');

// With callback — posts with approved comments
Post::query()->withCountWhereHas('comments', function ($q) {
    $q->where('approved', true);
});

// With operator/count — posts with 5+ comments
Post::query()->withCountWhereHas('comments', null, '>=', 5);

// OR variant — posts with comments OR tags
Post::query()
    ->withCountWhereHas('comments')
    ->orWithCountWhereHas('tags');
```

---

Collection Macros
-----------------

[](#collection-macros)

### paginate

[](#paginate)

```
->paginate(int $perPage, ?int $total = null, ?int $page = null, string $pageName = 'page'): LengthAwarePaginator
```

Paginate an in-memory collection.

```
$items = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

$paginated = $items->paginate(perPage: 5);
// Page 1: [1, 2, 3, 4, 5] — auto-resolves current page from request

$paginated = $items->paginate(perPage: 3, page: 2);
// Page 2: [4, 5, 6]

$paginated = $items->paginate(perPage: 5, total: 100);
// Override total count (useful for pre-sliced data)
```

---

API Responder
-------------

[](#api-responder)

The `HasApiResponse` trait (used by `BaseController`) provides response helpers for every HTTP status code. You can also use it in any controller:

```
use Anil\FastApiCrud\Concerns\HasApiResponse;

class MyController extends Controller
{
    use HasApiResponse;
}
```

### Core Methods

[](#core-methods)

```
// success(array $data = [], int $code = 200): JsonResponse
return $this->success(['key' => 'value']);          // {"data": {"key": "value"}}
return $this->success(['key' => 'value'], 201);    // Same with 201 status

// error(string $message = 'Something went wrong', array $data = [], int $status = 400): JsonResponse
return $this->error('Something went wrong');        // {"errors": [], "message": "Something went wrong"}
return $this->error('Not found', [], 404);          // Same with 404 status
```

The envelope keys (`data`, `errors`, `message`) are configurable via `fast-api.response.*` config.

### Complete Method Reference

[](#complete-method-reference)

All success methods accept `array $data = []`. All error methods accept `string $message` and `array $data = []`.

**1xx Informational:**

MethodStatusDefault Message`continue()`100—`switchingProtocols()`101—`processing()`102—`earlyHints()`103—**2xx Success:**

MethodStatusNotes`ok()`200—`created()`201—`accepted()`202—`nonAuthoritativeInformation()`203—`noContent()`204Returns `null` body`resetContent()`205—`partialContent()`206—`multiStatus()`207—`alreadyReported()`208—`imUsed()`226—**3xx Redirection:**

MethodStatus`multipleChoices()`300`movedPermanently()`301`found()`302`seeOther()`303`notModified()`304`useProxy()`305`temporaryRedirect()`307`permanentRedirect()`308**4xx Client Error:**

MethodStatusDefault Message`badRequest()`400Bad Request`unauthorized()`401Unauthorized`paymentRequired()`402Payment Required`forbidden()`403Forbidden`notFound()`404Not Found`methodNotAllowed()`405Method Not Allowed`notAcceptable()`406Not Acceptable`proxyAuthenticationRequired()`407Proxy Authentication Required`requestTimeout()`408Request Timeout`conflict()`409Conflict`gone()`410Gone`lengthRequired()`411Length Required`preconditionFailed()`412Precondition Failed`contentTooLarge()`413Content Too Large`uriTooLong()`414URI Too Long`unsupportedMediaType()`415Unsupported Media Type`rangeNotSatisfiable()`416Range Not Satisfiable`expectationFailed()`417Expectation Failed`imATeapot()`418I'm a teapot`misdirectedRequest()`421Misdirected Request`unprocessableContent()`422Unprocessable Content`locked()`423Locked`failedDependency()`424Failed Dependency`tooEarly()`425Too Early`upgradeRequired()`426Upgrade Required`preconditionRequired()`428Precondition Required`tooManyRequests()`429Too Many Requests`requestHeaderFieldsTooLarge()`431Request Header Fields Too Large`unavailableForLegalReasons()`451Unavailable For Legal Reasons**5xx Server Error:**

MethodStatusDefault Message`internalServerError()`500Internal Server Error`notImplemented()`501Not Implemented`badGateway()`502Bad Gateway`serviceUnavailable()`503Service Unavailable`gatewayTimeout()`504Gateway Timeout`httpVersionNotSupported()`505HTTP Version Not Supported`variantAlsoNegotiates()`506Variant Also Negotiates`insufficientStorage()`507Insufficient Storage`loopDetected()`508Loop Detected`notExtended()`510Not Extended`networkAuthenticationRequired()`511Network Authentication Required---

Helper Functions
----------------

[](#helper-functions)

Global helper functions available throughout your application (autoloaded via composer).

### Date &amp; Time

[](#date--time)

```
diffForHumans(?string $date): ?string
// diffForHumans('2025-01-15')           → "2 months ago"
// diffForHumans(null)                   → null

ymdDate(?string $date, string $format = 'Y-m-d'): ?string
// ymdDate('2025-01-15 14:30:00')        → "2025-01-15"
// ymdDate('2025-01-15', 'd/m/Y')        → "15/01/2025"

dateForReports(?string $date, string $format = 'Y-m-d H:i'): ?string
// dateForReports('2025-01-15 14:30:00') → "2025-01-15 14:30"
// Returns null on invalid date (doesn't throw)

toFormattedDateString(?string $date): ?string
// toFormattedDateString('2025-01-15')   → "January 15, 2025"

toDateString(?string $date): ?string
// toDateString('2025-01-15 14:30:00')   → "2025-01-15"

toDateTimeString(?string $date): ?string
// toDateTimeString('2025-01-15 14:30')  → "2025-01-15 14:30:00"

toTimeString(?string $date): ?string
// toTimeString('2025-01-15 14:30:00')   → "14:30:00"
```

### Duration

[](#duration)

```
parseTimeToSeconds(string $timeString): int|float
// parseTimeToSeconds('01:30:00')        → 5400    (H:i:s format)
// parseTimeToSeconds('30:45')           → 1845    (i:s format)
// parseTimeToSeconds('120')             → 120     (plain seconds)

formatDuration(int|float|null $duration, ?string $format = '%y %mo %d %h %m %s', string $separator = ' '): string
// formatDuration(3661)                  → "1h 1m 1s"
// formatDuration(90061)                 → "1d 1h 1m 1s"
// formatDuration(3661, '%h %m')         → "1h 1m"
// formatDuration(3661, '%h %m', '-')    → "1h-1m"
// formatDuration(null)                  → "0s"
// formatDuration(0)                     → "0s"
// formatDuration(-3661)                 → "1h 1m 1s"  (absolute value)
// Placeholders: %y=years, %mo=months, %d=days, %h=hours, %m=minutes, %s=seconds
```

### Filtering &amp; Sorting

[](#filtering--sorting)

```
filterValue(string $key = 'date'): ?string
// Reads from ?filters={"date":"2025-01-15"} query parameter
// filterValue('date')                   → "2025-01-15"
// filterValue('missing')                → null

arrayFilters(array|string|null $data): array
// arrayFilters('{"active":1,"name":""}')→ ['active' => 1]  (removes falsy)
// arrayFilters(['a' => 1, 'b' => null]) → ['a' => 1]
// arrayFilters(null)                    → []

flattenArray(array $data, int $depth = 0): array
// flattenArray(['a' => [1, 2], 'b' => [3]])  → [1, 2, 3]
// flattenArray(['a' => [1, [2]]], 1)          → [1, [2]]

sortDirection(): string
// Reads ?descending query parameter
// ?descending=true  → "ASC"
// ?descending=false → "DESC"

sortBy(): array|string|null
// Reads ?sort query parameter
// ?sort=name        → "name"
// ?sort[]=name&sort[]=id → ["name", "id"]
// (not set)         → null
```

### Utility

[](#utility)

```
uuid(): UuidInterface
// uuid()                                → Ramsey\Uuid\UuidInterface (v4)

slug(?string $text = null): ?string
// slug('Hello World')                   → "hello-world"
// slug(null)                            → null

relativePath(string $path): string
// relativePath('/var/www/app/Models/Post.php') → "app/Models/Post.php"
// (relative to base_path())

classShortName(string $param): ?string
// classShortName(App\Models\Post::class) → "Post"
// Uses reflection, throws ReflectionException if unresolvable
```

### Introspection

[](#introspection)

```
scopeMethods(object $class): array
// scopeMethods(new Post)                → ["scopeActive", "scopePublished", ...]
// Returns all public methods starting with "scope"

tableColumns(string|Model $table = 'users'): array
// tableColumns('posts')                 → ["id", "active", "desc", "name", ..., "created_at", "updated_at", "deleted_at"]
// tableColumns(Post::class)             → Same (accepts class-string)
// tableColumns(new Post)                → Same (accepts model instance)
// Columns sorted alphabetically, with id first and timestamps last

fillableCsv(string $model): string
// fillableCsv(Post::class)              → "name,desc,status,active"
// fillableCsv('posts')                  → All columns from table (if not a model class)

columnsCsv(string $model, string $separator = ','): string
// columnsCsv(Post::class)               → "id,active,desc,name,status,created_at,updated_at"
// columnsCsv(Post::class, '|')          → "id|active|desc|name|status|created_at|updated_at"
```

### Class Discovery

[](#class-discovery)

```
appClasses(string $path = 'App', array $excluding = []): array
// appClasses('Models')                   → ["App\Models\Post", "App\Models\User", ...]
// appClasses('Models', [App\Models\User::class])  → ["App\Models\Post", ...]

databaseClasses(?string $directory = null, array $excluding = []): array
// databaseClasses('seeders')             → ["Database\Seeders\PostSeeder", ...]
// databaseClasses()                      → All classes in database/ directory
// databaseClasses('factories', ['Database\Factories\UserFactory']) → Filtered list
```

---

Exceptions
----------

[](#exceptions)

### ApiException

[](#apiexception)

Custom exception that renders as JSON with debug info in development.

```
use Anil\FastApiCrud\Exceptions\ApiException;

throw new ApiException('Resource not found', 404);
```

**Production response:**

```
{
  "error": {
    "message": "Resource not found"
  }
}
```

**Debug response** (when `APP_DEBUG=true`):

```
{
  "error": {
    "message": "Resource not found",
    "file": "/app/Http/Controllers/PostController.php",
    "line": 42
  }
}
```

---

Permissions
-----------

[](#permissions)

Both `BaseController` and `BaseWebController` implement Laravel's `HasMiddleware` interface. Override the static `middleware()` method in your controller and call `permissionMiddleware()` with the slug to register Spatie permission middleware:

```
class PostController extends BaseController
{
    public static function middleware(): array
    {
        return static::permissionMiddleware('posts');
    }

    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}
```

`permissionMiddleware()` returns an empty array when `fast-api.permissions.enabled` is `false`, so toggling permissions off in config is safe without changing controller code.

---

Routes
------

[](#routes)

### Full API Route Setup

[](#full-api-route-setup)

```
use App\Http\Controllers\PostController;

// Standard CRUD
Route::get('posts', [PostController::class, 'index'])->name('posts.index');
Route::post('posts', [PostController::class, 'store'])->name('posts.store');
Route::get('posts/{id}', [PostController::class, 'show'])->name('posts.show');
Route::put('posts/{id}', [PostController::class, 'update'])->name('posts.update');
Route::delete('posts/{id}', [PostController::class, 'destroy'])->name('posts.destroy');

// Bulk delete
Route::post('posts/delete', [PostController::class, 'delete'])->name('posts.delete');

// Status & column
Route::put('posts/{id}/status-change', [PostController::class, 'changeStatus'])->name('posts.changeStatus');
Route::put('posts/{id}/status-change/{column}', [PostController::class, 'updateColumn'])->name('posts.updateColumn');

// Soft delete operations
Route::put('posts/{id}/restore', [PostController::class, 'restore'])->name('posts.restore');
Route::post('posts/restore-all', [PostController::class, 'restoreAll'])->name('posts.restoreAll');
Route::post('posts/{id}/force-delete', [PostController::class, 'permanentDelete'])->name('posts.permanentDelete');
```

### Full Web Route Setup

[](#full-web-route-setup)

```
use App\Http\Controllers\PostController;

// Standard resource routes (index, create, store, show, edit, update, destroy)
Route::resource('posts', PostController::class);

// Extended operations
Route::put('posts/{id}/status-change', [PostController::class, 'changeStatus'])->name('posts.changeStatus');
Route::put('posts/{id}/restore', [PostController::class, 'restore'])->name('posts.restore');
Route::post('posts/restore-all', [PostController::class, 'restoreAll'])->name('posts.restoreAll');
Route::post('posts/{id}/force-delete', [PostController::class, 'permanentDelete'])->name('posts.permanentDelete');
```

---

Pagination Utility
------------------

[](#pagination-utility)

The `Anil\FastApiCrud\Support\Pagination` class provides static helpers used internally by the macros. You can also use them directly:

```
use Anil\FastApiCrud\Support\Pagination;

Pagination::defaultPerPage();       // 15 (from config)
Pagination::maxPerPage();           // 100 (from config)
Pagination::requestedPerPage(15);   // Value of ?rowsPerPage or default
Pagination::resolvePerPage();       // Effective per-page (respects max, allow_all)

// Generic config helpers
Pagination::configInt('fast-api.pagination.default_per_page', 15);   // int
Pagination::configBool('fast-api.pagination.allow_all', true);       // bool
```

---

Enums
-----

[](#enums)

### PaginationType

[](#paginationtype)

```
use Anil\FastApiCrud\Enums\PaginationType;

PaginationType::LengthAware  // 'length-aware' — Standard pagination with total count
PaginationType::Simple        // 'simple'       — Simple pagination without total
PaginationType::Cursor        // 'cursor'       — Cursor-based pagination
PaginationType::None          // 'none'         — No pagination, returns all records
```

### CrudAction

[](#crudaction)

Used for permission middleware registration.

```
use Anil\FastApiCrud\Enums\CrudAction;

CrudAction::View          // 'view'
CrudAction::Store         // 'store'
CrudAction::Update        // 'update'
CrudAction::Delete        // 'delete'
CrudAction::ChangeStatus  // 'change-status'
CrudAction::Restore       // 'restore'
```

---

Full Example
------------

[](#full-example)

### Model

[](#model)

```
namespace App\Models;

use Anil\FastApiCrud\Concerns\HasDateScopes;
use Anil\FastApiCrud\Concerns\AnonymizesOnDelete;
use Anil\FastApiCrud\Contracts\HasPermissionSlug;
use Anil\FastApiCrud\Contracts\Searchable;
use Anil\FastApiCrud\Contracts\Sortable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model implements Searchable, Sortable, HasPermissionSlug
{
    use SoftDeletes, HasDateScopes, AnonymizesOnDelete;

    protected $fillable = ['name', 'desc', 'status', 'active', 'user_id'];

    // --- Contracts ---

    public function searchableColumns(): array
    {
        return ['name', 'desc', 'user:name,email'];
    }

    public function sortByDefaults(): array
    {
        return ['sortBy' => 'created_at', 'sortByDesc' => true];
    }

    public function getPermissionSlug(): string
    {
        return 'posts';
    }

    // --- Relations ---

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }

    // --- Scopes (callable via ?filters={"active":1}) ---

    public function scopeActive($query, int $active = 1)
    {
        return $query->where('active', $active);
    }

    // --- Lifecycle Hooks ---

    public function afterCreate(): void
    {
        if (request()->filled('tag_ids')) {
            $this->tags()->sync(request()->input('tag_ids'));
        }
    }

    public function afterUpdate(): void
    {
        if (request()->filled('tag_ids')) {
            $this->tags()->sync(request()->input('tag_ids'));
        }
    }
}
```

### API Controller

[](#api-controller)

```
namespace App\Http\Controllers\Api;

use Anil\FastApiCrud\Enums\PaginationType;
use Anil\FastApiCrud\Http\Controllers\BaseController;
use App\Http\Requests\Post\StorePostRequest;
use App\Http\Requests\Post\UpdatePostRequest;
use App\Http\Resources\Post\PostResource;
use App\Models\Post;

class PostController extends BaseController
{
    protected PaginationType $paginationType = PaginationType::LengthAware;

    protected array $with = ['user', 'tags'];
    protected array $withCount = ['tags'];
    protected array $load = ['user', 'tags', 'tags.posts'];
    protected array $scopes = ['active'];

    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            resource: PostResource::class,
        );
    }
}
```

### Web Controller

[](#web-controller)

```
namespace App\Http\Controllers\Web;

use Anil\FastApiCrud\Http\Controllers\BaseWebController;
use App\Http\Requests\Post\StorePostRequest;
use App\Http\Requests\Post\UpdatePostRequest;
use App\Models\Post;

class PostController extends BaseWebController
{
    protected array $with = ['user', 'tags'];
    protected array $load = ['user', 'tags'];

    public function __construct()
    {
        parent::__construct(
            model: Post::class,
            storeRequest: StorePostRequest::class,
            updateRequest: UpdatePostRequest::class,
            viewPrefix: 'posts',
            routePrefix: 'posts',
            resourceName: 'post',
            collectionName: 'posts',
        );
    }

    protected function storeSuccessMessage(): string
    {
        return __('Post created successfully!');
    }
}
```

---

License
-------

[](#license)

MIT

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance65

Regular maintenance activity

Popularity32

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 97.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 ~49 days

Recently: every ~207 days

Total

24

Last Release

34d ago

Major Versions

v1.0.3 → v2.0.02023-05-05

1.x-dev → v2.0.12023-05-10

2.x-dev → 3.x-dev2026-05-29

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/16583802?v=4)[Er. Anil Kumar Thakur](/maintainers/anilkumarthakur60)[@anilkumarthakur60](https://github.com/anilkumarthakur60)

---

Top Contributors

[![anilkumarthakur60](https://avatars.githubusercontent.com/u/16583802?v=4)](https://github.com/anilkumarthakur60 "anilkumarthakur60 (428 commits)")[![StyleCIBot](https://avatars.githubusercontent.com/u/11048387?v=4)](https://github.com/StyleCIBot "StyleCIBot (6 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (4 commits)")[![codesandbot](https://avatars.githubusercontent.com/u/127971067?v=4)](https://github.com/codesandbot "codesandbot (1 commits)")

---

Tags

anil-kumar-thakurlaravellaravel-api-developmentlaravel-rest-apirest-apirest-api-crud-applicationapilaravelfastcrudcontrolleranilapi-crudapi-crud-controllerfast-api-crud

###  Code Quality

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/anil-fast-api-crud/health.svg)

```
[![Health](https://phpackages.com/badges/anil-fast-api-crud/health.svg)](https://phpackages.com/packages/anil-fast-api-crud)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M131](/packages/roots-acorn)[api-platform/laravel

API Platform support for Laravel

58170.8k14](/packages/api-platform-laravel)[laravel/cashier

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

2.6k29.9M146](/packages/laravel-cashier)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M131](/packages/laravel-pulse)[nuwave/lighthouse

A framework for serving GraphQL from Laravel

3.5k11.8M117](/packages/nuwave-lighthouse)

PHPackages © 2026

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