singlequote/laravel-api-resource

2.2.7 2025-09-17 09:52 UTC

README

Latest Version on Packagist Total Downloads

A practical Laravel package designed to streamline API development by automating resource generation and providing a powerful, out-of-the-box filtering system.

This package accelerates your workflow with two core features:

  1. Rapid Scaffolding: Use the php artisan make:api-resource command to intelligently generate a full set of API files (Controller, Actions, Requests, and Resource) from your existing models.
  2. Powerful Filtering: Equip your API endpoints with a comprehensive set of filters from the moment you install it. Sort, search, and filter resources with ease without any initial setup.

Table of Contents

Installation & Setup

1. Install the package via Composer:

composer require singlequote/laravel-api-resource

2. (Optional) Publish Files:

You can publish the configuration and stub files to customize the package's behavior and generated file templates.

Publish the config file:

php artisan vendor:publish --tag=laravel-api-resource-config

Publish the stub files:

php artisan vendor:publish --tag=laravel-api-resource-stubs

Usage

1. Generating a Full API Resource

Use the make:api-resource Artisan command to generate all the necessary files for a model's API endpoint.

php artisan make:api-resource User

This single command creates the following file structure, ready for you to add your business logic:

App/Http/Controllers
└── Api/UserController.php

App/Actions/Users
├── DeleteUserAction.php
├── IndexUserAction.php
├── ShowUserAction.php
├── StoreUserAction.php
└── UpdateUserAction.php

App/Http/Requests/Users
├── IndexUserRequest.php
├── ShowUserRequest.php
├── StoreUserRequest.php
└── UpdateUserRequest.php

App/Http/Resources
└── UserResource.php

Finally, add the generated route to your routes/api.php file:

use App\Http\Controllers\Api\UserController;

Route::apiResource('users', UserController::class);

2. Using the API Filters

To enable the powerful filtering capabilities, simply add the HasApi trait to your model.

use Illuminate\Database\Eloquent\Model;
use SingleQuote\LaravelApiResource\Traits\HasApi;

class User extends Model
{
    use HasApi;
    
    // ...
}

You can now use a wide range of query parameters to filter your API results directly from the URL. See the API Filtering Reference below for a full list of available methods.

3. Customizing the Resource Response

The package provides helpers to easily customize your JSON response. For instance, you can use the ApiPolicyService to automatically include the results of your model's policies.

In your UserResource.php:

use SingleQuote\LaravelApiResource\Service\ApiPolicyService;
use Illuminate\Http\Request;

public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        // ...
        'policies' => ApiPolicyService::defaults($this->resource, ['sendInvite']),
    ];
}

API Filtering Reference

All examples use the Ziggy package for clean URL generation. A manual URL example is provided for reference.

Helper Value Type Description
limit number Sets the number of results per page.
search array Searches specified columns for a query.
where array Adds a "where" clause to the query.
orWhere array Adds an "or where" clause.
whereIn array Filters by a column's value within an array.
whereNotIn array Filters by a column's value not in an array.
whereNull string Finds records where a column is NULL.
whereNotNull string Finds records where a column is not NULL.
has array Filters based on the existence of a relationship.
doesntHave array Filters based on the absence of a relationship.
whereRelation array Queries a relationship with a where condition.
with array Eager loads relationships.
withCount array Counts the results of a given relationship.
select array Selects specific columns to return.
orderBy string Sorts the results in ascending order.
orderByDesc string Sorts the results in descending order.

limit

The default limit is 1000. You can change this in the config file or override it per request.

axios.get(route('api.users.index', { limit: 100 }));
// GET /api/users?limit=100

search

Search for a query within specified columns. Use * to search all fillable columns.

axios.get(route('api.users.index', {
    search: {
        fields: ['name', 'email'],
        query: "john"
    }
}));
// GET /api/users?search[fields][0]=name&search[fields][1]=email&search[query]=john

where

Add "where" clauses. You can also provide an operator (gt, lt, sw, etc.).

Operator Shorthand
startsWith sw
endsWith ew
notContains nin
contains in
equals eq
notEqual neq
greater gt
greaterEquals gte
lesser lt
lesserEquals lte
axios.get(route('api.users.index', {
    where: {
        date_of_birth: { gt: "1995-01-31" }
    }
}));
// GET /api/users?where[date_of_birth][gt]=1995-01-31

whereIn / whereNotIn

Verifies that a column's value is (or is not) within a given array.

axios.get(route('api.users.index', {
    whereIn: { role: ['admin', 'employee'] }
}));
// GET /api/users?whereIn[role][0]=admin&whereIn[role][1]=employee

whereNull / whereNotNull

Verifies that a column's value is NULL or not NULL.

axios.get(route('api.users.index', { whereNull: "email_verified_at" }));
// GET /api/users?whereNull=email_verified_at

has / doesntHave

Limit results based on the existence of a relationship. You can also add nested conditions.

axios.get(route('api.users.index', {
    has: {
        roles: {
            whereIn: { id: [1, 2] }
        }
    }
}));
// GET /api/users?has[roles][whereIn][id][0]=1&has[roles][whereIn][id][1]=2

whereRelation

Query for a relationship's existence with a simple where condition.

axios.get(route('api.users.index', {
    whereRelation: {
        roles: { name: 'admin' }
    }
}));
// GET /api/users?whereRelation[roles][name]=admin

with

Eager load relationships to avoid N+1 query problems.

axios.get(route('api.users.index', {
    with: {
        roles: {
            select: ['id', 'name']
        }
    }
}));
// GET /api/users?with[roles][select][0]=id&with[roles][select][1]=name

withCount

Count the number of results from a relationship without loading them.

axios.get(route('api.users.index', { withCount: ['posts'] }));
// GET /api/users?withCount[0]=posts

Note: To include the count in your response, you must manually add the posts_count attribute to your resource's toArray method.

// In app/Http/Resources/UserResource.php
public function toArray(Request $request): array
{
    return [
        // ... other attributes
        'posts_count' => $this->whenCounted('posts'),
    ];
}

select

Specify which columns to retrieve to keep responses lean.

axios.get(route('api.users.index', { select: ['id', 'name'] }));
// GET /api/users?select[0]=id&select[1]=name

orderBy / orderByDesc

Sort the results by a given column, including columns on related models.

axios.get(route('api.users.index', { orderBy: 'roles.name' }));
// GET /api/users?orderBy=roles.name

Custom Orderable Columns

To make custom columns (e.g., from withCount or withSum) sortable, add them to the $apiOrderBy property on your model.

// In your Product.php model
class Product extends Model
{
    public array $apiOrderBy = [
        'articles_sum_price', // From a withSum query
    ];
}

Now you can sort by this custom column: GET /api/products?orderBy=articles_sum_price

Contributing

Please see CONTRIBUTING.md for details.

Postcardware

You're free to use this package, but if it makes it to your production environment, we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Quotec, Traktieweg 8c 8304 BA, Emmeloord, Netherlands.

Credits

License

The MIT License (MIT). Please see License File for more information.