Installation
composer require laravel-json-api/eloquent
Publish the config (optional but recommended for customization):
php artisan vendor:publish --provider="JsonApi\Laravel\JsonApiServiceProvider" --tag=config
Basic Usage
Define a resource class (e.g., app/Http/Resources/UserResource.php):
namespace App\Http\Resources;
use JsonApi\Laravel\ResourceObject;
use App\Models\User;
class UserResource extends ResourceObject
{
public static $resourceKey = 'users';
public static $shortName = 'user';
public function getAttributes(User $user)
{
return [
'name' => $user->name,
'email' => $user->email,
];
}
}
First API Response In a controller:
use App\Http\Resources\UserResource;
use App\Models\User;
public function index()
{
return UserResource::collection(User::all());
}
This returns a standardized JSON:API response:
{
"data": [
{
"type": "users",
"id": "1",
"attributes": {
"name": "John Doe",
"email": "[email protected]"
}
}
]
}
Key Files to Review
config/json-api.php: Global configuration (e.g., default meta fields, pagination).app/Http/Resources/: Your resource classes.JsonApi\Laravel\ResourceObject: Base class for customization.Resources directory with subdirectories (e.g., Users, Posts) for scalability.abstract class BaseResource extends ResourceObject
{
public function getMeta()
{
return ['created_at' => $this->resource->created_at];
}
}
getRelationshipData():
public function getRelationshipData($key)
{
return $this->whenLoaded($key, function () use ($key) {
return $this->{$key} ? new PostResource($this->{$key}) : null;
});
}
ResourceObject::collection():
public function getRelationshipData($key)
{
return $this->whenLoaded($key, function () use ($key) {
return PostResource::collection($this->{$key});
});
}
JsonApi\Laravel\Query\QueryBuilder in controllers:
use JsonApi\Laravel\Query\QueryBuilder;
public function index(Request $request)
{
$query = QueryBuilder::for(User::class)
->allowedFilters(['name', 'email'])
->allowedSorts(['name', 'created_at'])
->paginate();
return UserResource::collection($query->get());
}
JsonApi\Laravel\Query\Filter:
public function apply($query, $value)
{
return $query->where('name', 'like', "%{$value}%");
}
getMeta() in resources:
public function getMeta()
{
return [
'custom_field' => $this->resource->custom_field,
'links' => [
'self' => route('users.show', $this->resource),
],
];
}
config/json-api.php:
'meta' => [
'version' => '1.0',
],
$user = new User();
$resource = new UserResource($user);
$this->assertEquals('John Doe', $resource->name);
JsonApiTestCase (if provided) or Http::fake():
$response = $this->getJson('/api/users');
$response->assertJsonStructure([
'data' => [
'*' => ['type', 'id', 'attributes']
]
]);
N+1 Queries
with() in queries or whenLoaded() in resources:
$users = User::with('posts')->get();
// OR
$resource->whenLoaded('posts', fn() => PostResource::collection($this->posts));
Circular References
User->posts and Post->user) cause infinite loops.shouldSerialize() or exclude in getRelationshipData():
public function getRelationshipData($key)
{
return $this->whenLoaded($key, function () use ($key) {
return $key === 'user' ? null : PostResource::collection($this->posts);
});
}
ID Type Mismatch
getId():
public function getId($model)
{
return (string) $model->id;
}
Pagination Conflicts
paginate()) may not align with JSON:API.JsonApi\Laravel\Query\QueryBuilder for consistent pagination:
$query->paginate(10); // Returns JSON:API-compliant pagination headers.
'debug' => true in config/json-api.php to log resource serialization.dd($resource->resolve()) to see raw output before JSON encoding.Content-Type: application/vnd.api+json is set in responses.Custom Serializers
Override JsonApi\Laravel\Serializers\ResourceSerializer for global changes (e.g., date formatting):
public function serialize($resource)
{
$data = parent::serialize($resource);
$data['attributes']['created_at'] = $resource->created_at->toIso8601String();
return $data;
}
Middleware for Auth Use middleware to attach auth data to meta:
public function handle($request, Closure $next)
{
$response = $next($request);
$response->getData()->setMeta('auth', ['user_id' => auth()->id()]);
return $response;
}
Dynamic Resource Keys
Use closures in getResourceKey() for dynamic keys:
public function getResourceKey($model)
{
return $model->is_admin ? 'admins' : 'users';
}
return Cache::remember("users.{$request->query()}", now()->addHours(1), function () {
return UserResource::collection(User::all());
});
JsonApi\Laravel\LazyLoad for large datasets:
$resource = new UserResource(User::find(1));
$resource->lazyLoad('posts');
How can I help you explore Laravel packages today?