API Resources provide a transformation layer between your Eloquent models and the JSON responses. They give you full control over how your data is structured.
Why Use Resources?
php// Without resources - exposes all model data return User::find(1); // {"id":1,"name":"John","email":"john@example.com","password":"$2y$...","remember_token":"abc..."} // With resources - control what's exposed return new UserResource(User::find(1)); // {"id":1,"name":"John","email":"john@example.com"}
Creating Resources
bash# Single resource php artisan make:resource UserResource # Collection resource (for arrays) php artisan make:resource UserCollection
Basic Resource
php<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource { /** * Transform the resource into an array. */ public function toArray(Request $request): array { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, 'created_at' => $this->created_at->toISOString(), ]; } }
Usage:
phpuse App\Http\Resources\UserResource; // Single resource public function show(User $user) { return new UserResource($user); } // Collection public function index() { return UserResource::collection(User::all()); } // With pagination public function index() { return UserResource::collection(User::paginate(15)); }
Resource with Relationships
phpclass PostResource extends JsonResource { public function toArray(Request $request): array { return [ 'id' => $this->id, 'title' => $this->title, 'body' => $this->body, 'author' => new UserResource($this->user), 'comments' => CommentResource::collection($this->comments), 'tags' => TagResource::collection($this->tags), ]; } }
Conditional Attributes
phpclass PostResource extends JsonResource { public function toArray(Request $request): array { return [ 'id' => $this->id, 'title' => $this->title, // Only include if loaded (prevents N+1) 'author' => new UserResource($this->whenLoaded('user')), 'comments' => CommentResource::collection($this->whenLoaded('comments')), // Conditional based on value 'secret' => $this->when($request->user()?->isAdmin(), $this->secret), // Conditional based on request 'body' => $this->when($request->has('include_body'), $this->body), // Merge conditionally $this->mergeWhen($request->user()?->isAdmin(), [ 'internal_id' => $this->internal_id, 'notes' => $this->admin_notes, ]), ]; } }
Resource Collections
For customizing collection responses:
php<?php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\ResourceCollection; class PostCollection extends ResourceCollection { /** * The resource that this resource collects. */ public $collects = PostResource::class; /** * Transform the resource collection into an array. */ public function toArray(Request $request): array { return [ 'data' => $this->collection, 'meta' => [ 'total' => $this->collection->count(), 'has_more' => $this->hasMorePages(), ], ]; } }
Adding Meta Data
phpclass PostResource extends JsonResource { public function toArray(Request $request): array { return [ 'id' => $this->id, 'title' => $this->title, ]; } public function with(Request $request): array { return [ 'meta' => [ 'version' => '1.0', 'generated_at' => now()->toISOString(), ], ]; } }
Response:
json{ "data": { "id": 1, "title": "My Post" }, "meta": { "version": "1.0", "generated_at": "2024-01-15T10:30:00.000Z" } }
Customizing the Response
phpclass PostResource extends JsonResource { public function withResponse(Request $request, JsonResponse $response): void { $response->header('X-Resource-Version', '1.0'); if ($this->is_premium) { $response->header('X-Premium-Content', 'true'); } } }
Wrapping Data
By default, resources wrap data in a data key:
json{ "data": { "id": 1, "name": "John" } }
Disable wrapping:
php// In AppServiceProvider boot() JsonResource::withoutWrapping(); // Or per resource class UserResource extends JsonResource { public static $wrap = null; // Disable public static $wrap = 'user'; // Custom key }
Complete Example
phpclass PostResource extends JsonResource { public function toArray(Request $request): array { return [ 'id' => $this->id, 'title' => $this->title, 'slug' => $this->slug, 'excerpt' => Str::limit($this->body, 150), 'body' => $this->when($request->routeIs('posts.show'), $this->body), 'featured_image' => $this->featured_image ? asset('storage/' . $this->featured_image) : null, 'is_published' => $this->published_at !== null, 'published_at' => $this->published_at?->toISOString(), 'author' => new UserResource($this->whenLoaded('user')), 'category' => new CategoryResource($this->whenLoaded('category')), 'tags' => TagResource::collection($this->whenLoaded('tags')), 'comments_count' => $this->whenCounted('comments'), 'links' => [ 'self' => route('api.posts.show', $this), ], ]; } }