Installation
composer require laravel-json-api/core
No publisher or service provider is required—this package is purely contracts and base classes.
First Use Case
Use the JsonApiResource trait to create a JSON:API-compliant resource:
use LaravelJsonApi\Core\Contracts\JsonApiResource;
use LaravelJsonApi\Core\JsonApiResource as BaseJsonApiResource;
class Post extends BaseJsonApiResource implements JsonApiResource
{
public static function type(): string
{
return 'posts';
}
public function attributes(): array
{
return [
'title' => $this->title,
'body' => $this->body,
];
}
}
Where to Look First
LaravelJsonApi\Core\Contracts\JsonApiResource (defines required methods).LaravelJsonApi\Core\JsonApiResource (default implementation).LaravelJsonApi\Core\Exceptions\JsonApiException (for custom error handling).Resource Definition
Extend JsonApiResource and implement type() and attributes():
class Comment extends JsonApiResource
{
public static function type(): string { return 'comments'; }
public function attributes(): array { return ['body' => $this]; }
}
Relationships
Use relationships() to define belongs-to/many relationships:
public function relationships(): array
{
return [
'author' => new ToOneRelationship(
new AuthorResource(),
'author_id'
),
'tags' => new ToManyRelationship(
new TagResource(),
'tags'
),
];
}
Pagination & Meta
Leverage JsonApiResource::meta() and JsonApiResource::paginate() for compliance:
public function meta(): array
{
return ['published_at' => $this->created_at];
}
Custom Serialization
Override toArray() for non-standard JSON:API responses:
public function toArray($request): array
{
return [
'data' => [
'type' => 'posts',
'id' => $this->id,
'attributes' => $this->attributes(),
'links' => ['self' => route('posts.show', $this)],
],
];
}
laravel-json-api/v8 for full JSON:API support.JsonApiResource to test serialization logic:
$resource = Mockery::mock(Post::class);
$resource->shouldReceive('attributes')->andReturn(['title' => 'Test']);
$this->assertEquals(['title' => 'Test'], $resource->attributes());
phpdoc to auto-generate OpenAPI specs for resources.Missing type()
Forgetting to implement type() throws JsonApiException during serialization.
Fix: Always define type() as a static method returning the resource’s plural name (e.g., 'posts').
Circular Relationships
Nested relationships without guards cause infinite loops.
Fix: Use LaravelJsonApi\Core\Contracts\Relationship and implement lazy-loading:
public function author()
{
return $this->belongsTo(Author::class)->withDefault();
}
Attribute Whitelisting
Undeclared attributes in attributes() are silently ignored.
Tip: Use protected $hidden or protected $visible to control exposure.
Enable JSON:API Debugging
Add to config/app.php:
'json-api' => [
'debug' => env('JSON_API_DEBUG', false),
],
Logs malformed responses to storage/logs/json-api.log.
Common Exceptions
JsonApiException: Invalid resource structure (e.g., missing id or type).RuntimeException: Failed relationship resolution.Custom Resource Classes
Extend JsonApiResource to add shared logic:
abstract class BaseResource extends JsonApiResource
{
public function meta(): array
{
return ['updated_at' => $this->updated_at];
}
}
Middleware for Validation Validate incoming JSON:API requests with:
use LaravelJsonApi\Core\Validation\JsonApiValidator;
$validator = new JsonApiValidator($request);
if ($validator->fails()) {
throw new JsonApiException($validator->errors());
}
Testing Helpers
Use JsonApiTestCase (from laravel-json-api/v8) to assert responses:
$this->assertJsonApiResponse($response)
->hasDataOfType('posts')
->hasAttributes(['title']);
How can I help you explore Laravel packages today?