Laravel 13 JSON:API Resources
Agent Workflow (MANDATORY)
Before ANY implementation, spawn 3 agents in parallel, one Agent call each with a name:
- fuse-ai-pilot:explore-codebase - Inventory existing
JsonResourceclasses to migrate - fuse-ai-pilot:research-expert - Check JSON:API v1.1 spec for required headers and structure
- mcp__context7__query-docs - Pull
laravel.com/docs/13.x/eloquent-resourcesexamples
After implementation, run fuse-ai-pilot:sniper for validation.
Overview
| Feature | Description |
|---------|-------------|
| JsonApiResource | Base class extending JsonResource with spec compliance |
| Content-Type | Auto-sets application/vnd.api+json |
| Sparse fieldsets | ?fields[posts]=title,created_at |
| Inclusion | ?include=author,comments with included array |
| Resource identifiers | {"id":"1","type":"posts"} in relationships |
| Links / meta | toLinks() / toMeta() overrides |
Critical Rules
- Extend
JsonApiResource- Never roll your own JSON:API serializer; the base class handles spec edge cases - Type is derived from the class name (
PostResource→posts) - overridetoType()only when it must differ - Use
$attributes/toAttributes()nottoArray()- JSON:API splits attributes from identifiers; mixing them breaks compliance - Whitelist relationships - Declare
$relationships(ortoRelationships()) with only the relations clients may include - Respect Content-Type - Clients sending JSON:API requests MUST use
Accept: application/vnd.api+json
Architecture
app/Http/Resources/
├── PostResource.php # extends JsonApiResource → type "posts"
├── UserResource.php # extends JsonApiResource → type "users"
└── CommentResource.php # extends JsonApiResource → type "comments"
app/Http/Controllers/
└── Api/PostController.php # returns PostResource::collection($posts)
→ See PostResource.php.md for full example
Reference Guide
| Topic | Reference | When to Consult |
|-------|-----------|-----------------|
| Base resource class | resources.md | Structuring JsonApiResource subclasses |
| Sparse fieldsets | sparse-fieldsets.md | Implementing fields[type]=a,b |
| Relationships | relationships.md | Inclusion + identifiers + links |
Templates
| Template | When to Use | |----------|-------------| | PostResource.php.md | Resource with belongsTo + hasMany | | UserResource.php.md | Simple resource with sparse fields |
Quick Reference
Minimal resource
// php artisan make:resource PostResource --json-api
use Illuminate\Http\Resources\JsonApi\JsonApiResource;
class PostResource extends JsonApiResource
{
public $attributes = ['title', 'body'];
public $relationships = ['author', 'comments'];
}
Controller
return PostResource::collection(Post::with('author')->get());
→ See PostResource.php.md for complete example
Best Practices
DO
- Eager-load relationships used in
includeto avoid N+1 (?include=author→with('author')) - Document supported
includeandfieldsparameters in your OpenAPI spec - Override
toType()only when the derived type must differ from the class name (e.g.AuthorResourcewrappingUser) - Use
toLinks()to exposeself/relatedlinks
DON'T
- Don't return a JSON:API response without the
JsonApiResourcebase class - manual JSON breaks subtle spec rules (e.g., null vs empty data) - Don't include relationships not whitelisted in
$relationships/toRelationships()- silent ignoring keeps APIs predictable - Don't mix
toArray()andtoAttributes()- the JSON:API base class expects the latter - Don't forget to set the response Content-Type when bypassing resources (e.g., custom errors) - clients may reject the response