Laravel is one of the most powerful and expressive PHP frameworks, and one of its most valuable features for API development is the use of API Resources and Resource Collections. When building REST APIs, it is important to structure your data output consistently, securely, and efficiently. API Resource Collections help you accomplish this by transforming lists of data into well-structured JSON responses. Instead of returning raw Eloquent models or arrays, you can use Resources and Collections to ensure clean format, better control, pagination support, additional metadata, and maintainable output transformation logic.
This explains everything you need to know about API Resource Collections in Laravel. You will learn what they are, why they are important, how they differ from single resources, how to create them, how to format response lists, how to attach metadata, how to work with pagination, how to nest resources, how to modify collections globally, how to filter output fields, and how to follow industry standards like JSON:API style formatting.
By the end of this article, you will be able to build clean, scalable, and production-ready API responses using Laravel Resource Collections.
Understanding What API Resource Collections Are
In Laravel, an API Resource is a class that transforms a single model instance into a structured array format. A Resource Collection, however, is used for lists of data such as:
- all products
- all users
- all posts
- all orders
- all categories
A Collection ensures your API returns consistent JSON across the entire application.
For example, instead of getting raw Eloquent output like:
[
{
"id": 1,
"name": "Laptop",
"price": 1200,
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-15T10:00:00Z"
},
...
]
A Resource Collection allows you to define exactly:
- which fields to show
- how to format them
- how to add metadata
- how to shape pagination
The Resource Collection becomes the official structure of your API’s response.
Why API Resource Collections Are Important
Modern APIs require:
- consistent response structures
- secure data exposure
- ability to hide sensitive attributes
- flexibility in formatting
- easy pagination
- additional metadata like counts
- nested relationships
Raw Eloquent responses can leak sensitive fields like passwords or internal status flags. Collections help prevent that.
Collections also improve maintainability. Instead of formatting output in controllers or repeatedly writing array transformations, all formatting logic lives inside the Resource class.
How Resource Collections Work in Laravel
Laravel comes with two main ways to create resource collections:
- Using the
collection()method of a Resource - Creating a dedicated Resource Collection class
Example 1: using collection method
return ProductResource::collection(Product::all());
Example 2: using a dedicated collection
return new ProductCollection(Product::paginate(10));
Both approaches format output consistently, but dedicated collection classes offer more control.
Creating a Resource for API Output
First, create a resource for single items.
php artisan make:resource ProductResource
This generates:
app/Http/Resources/ProductResource.php
Inside:
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->name,
'price' => number_format($this->price, 2),
'created' => $this->created_at->toDateString()
];
}
Now every Product will be formatted like this.
Creating a Resource Collection
Create a dedicated collection class:
php artisan make:resource ProductCollection
It generates:
app/Http/Resources/ProductCollection.php
Default structure:
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
This is where you control:
- output structure
- metadata
- pagination
- links
- wrapper names
Returning a Collection in a Controller
Example controller method:
public function index()
{
return new ProductCollection(Product::all());
}
Or with pagination:
return new ProductCollection(Product::paginate(10));
Laravel automatically handles pagination meta fields.
Using Resource Collections With API Routes
Routes:
Route::get('/products', [ProductController::class, 'index']);
Controller:
return ProductResource::collection(Product::all());
Or dedicated:
return new ProductCollection(Product::paginate());
Both approaches are valid.
How Resource Collections Improve Consistency
Without collections, you might accidentally return:
- arrays
- objects
- mixed formats
- inconsistent keys
- uppercase/lowercase mismatches
With Resource Collections, every list of products always has the same structure.
Transforming the Data Structure of Collections
You can transform each item inside the collection:
public function toArray($request)
{
return [
'products' => $this->collection->transform(function ($product) {
return [
'id' => $product->id,
'title' => $product->name,
'price' => $product->price,
];
}),
];
}
This ensures all items follow the same format.
Adding Metadata to Resource Collections
You can add extra fields beyond data.
Example:
public function toArray($request)
{
return [
'count' => $this->collection->count(),
'items' => ProductResource::collection($this->collection),
];
}
Metadata examples:
- total items
- filters applied
- user role
- message
- version number
Using the with Method to Add Additional Data
You can use the with() method:
public function with($request)
{
return [
'status' => 'success',
'api_version' => '1.0',
];
}
Final output:
{
"data": [...],
"status": "success",
"api_version": "1.0"
}
Working With Pagination in Resource Collections
Resource Collections fully support pagination.
Controller:
return new ProductCollection(Product::paginate(10));
Response automatically includes:
- total
- per_page
- current_page
- last_page
- links
You can customize them:
public function toArray($request)
{
return [
'meta' => [
'total_items' => $this->total(),
'per_page' => $this->perPage(),
'current_page' => $this->currentPage(),
],
'data' => ProductResource::collection($this->collection),
];
}
Wrapping Collections in Custom Keys
Default resource collections wrap data in a data key.
But you can customize the key:
public static $wrap = 'products';
Now your output becomes:
{
"products": [...]
}
Or remove wrapping:
public static $wrap = null;
Filtering Resource Output Fields
You may want different fields depending on:
- admin vs normal user
- mobile vs web
- authenticated vs guest
Use conditional output:
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->name,
'price' => $this->price,
'secret' => $this->when(auth()->user()?->isAdmin(), $this->secret),
];
}
Only admins get the secret field.
Conditional Relationships Inside Collections
Laravel allows including relationships only when needed.
'reviews' => ReviewResource::collection($this->whenLoaded('reviews')),
Load relationship only when:
Product::with('reviews')->get();
This keeps your API lean.
Nesting Resources Inside Collections
You can nest multiple resources:
return [
'id' => $this->id,
'title' => $this->name,
'brand' => new BrandResource($this->brand),
'categories' => CategoryResource::collection($this->categories),
];
Transforming Paginated Data With Resources
Paginated collections automatically apply Resources to every item.
return ProductResource::collection(Product::paginate(10));
Or use a custom collection:
return new ProductCollection(Product::paginate(10));
Complex Example: Adding Links to Collections
Hypermedia-style formatting:
public function toArray($request)
{
return [
'data' => ProductResource::collection($this->collection),
'links' => [
'self' => url('/api/products'),
'next' => $this->nextPageUrl(),
'prev' => $this->previousPageUrl(),
],
];
}
Customizing the toResponse Method
For total control:
public function toResponse($request)
{
return response()->json([
'status' => 'success',
'payload' => $this->toArray($request),
]);
}
Using Anonymous Resource Collections
Laravel allows resource-only collections:
return ProductResource::collection($products);
This is useful for simple collections without metadata.
Using Resource Collections for Large Datasets
Collections improve:
- structure
- readability
- memory usage (with pagination)
- consistency
When handling large datasets, always use pagination.
Updating Resource Collections When API Changes
You can update the Resource without modifying controllers or routes.
This makes Resources ideal for versioning.
Example change:
Add a field:
'stock' => $this->stock
All controllers instantly return updated results.
Versioning Your API Using Collections
Versioning example:
app/Http/Resources/V1/ProductResource
app/Http/Resources/V2/ProductResource
Route version:
Route::prefix('v2')->get('/products', ...);
Each version can have different fields.
Using Resource Collections With Transformers
Resources can work side-by-side with:
- Services
- Repositories
- DTOs
- Transformers
This helps with domain-driven designs.
Converting Collection to JSON Style Structures
For JSON:API style:
'id' => (string) $this->id,
'type' => 'product',
'attributes' => [
'title' => $this->name,
'price' => $this->price,
]
Collections can enforce JSON:API formatting.
Returning Minimal Output or Extended Output
Use conditional checks:
return [
'id' => $this->id,
'title' => $this->name,
'details' => $this->when($request->query('details'), [
'description' => $this->description,
'stock' => $this->stock
])
];
Useful for mobile apps.
Adding Sorting and Filtering Inside Collections
You can modify collection data:
$this->collection->sortBy('price');
Or apply filtering:
$this->collection->filter(function ($item) {
return $item->price > 100;
});
Leave a Reply