Laravel API Resources: JSON Responses That Don't Suck 🎨
Laravel API Resources: JSON Responses That Don't Suck 🎨
You know what's embarrassing? Returning raw Eloquent models in your API and accidentally exposing password hashes, internal IDs, and timestamps nobody asked for.
Been there. Done that. Got the security audit to prove it.
Let's talk about Laravel API Resources - the feature that'll make your JSON responses actually look professional!
The Problem: Your API is Leaking Info 💧
Here's what we've all done at least once:
// Controller
public function show(User $user)
{
return $user; // 🚨 YOLO mode activated
}
What the API returns:
{
"id": 42,
"name": "John Doe",
"email": "[email protected]",
"password": "$2y$10$92IXU...", // 😱 Oops!
"remember_token": "abc123...",
"email_verified_at": "2026-01-15...",
"created_at": "2026-01-01...",
"updated_at": "2026-01-20...",
"deleted_at": null,
"internal_notes": "Complains a lot",
"is_banned": false
}
Yeah... maybe your users don't need to see ALL of that!
Enter API Resources: Your JSON Makeover Tool 💄
API Resources are like Photoshop for your database models. They let you control exactly what gets shown and how it looks!
Create a resource:
php artisan make:resource UserResource
Transform your data:
// app/Http/Resources/UserResource.php
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'joined' => $this->created_at->format('Y-m-d'),
// Notice what's NOT here? 👆
];
}
}
Use it in your controller:
public function show(User $user)
{
return new UserResource($user);
}
What you get now:
{
"data": {
"id": 42,
"name": "John Doe",
"email": "[email protected]",
"joined": "2026-01-01"
}
}
Clean! Secure! Professional! 🎯
Conditional Fields: Show Me If You Dare 🎭
Sometimes you want to show different fields to different users. Like showing admin-only data.
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
// Only show to admins
'is_banned' => $this->when(
$request->user()?->isAdmin(),
$this->is_banned
),
// Only show user's own data
'api_token' => $this->when(
$request->user()?->id === $this->id,
$this->api_token
),
];
}
}
Real Talk: This is how you build multi-tenant APIs without going insane!
Relationships: Because APIs Have Friends Too 👥
Want to include related data? Resources got you covered!
class PostResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => $this->content,
// Include the author as a resource
'author' => new UserResource($this->whenLoaded('author')),
// Include comments collection
'comments' => CommentResource::collection(
$this->whenLoaded('comments')
),
// Just the count
'likes_count' => $this->when(
isset($this->likes_count),
$this->likes_count
),
];
}
}
Pro Tip: Always use whenLoaded() for relationships! It prevents N+1 queries and doesn't break when you forget to eager load. Ask me how I learned this... 😅
Collections: One vs Many 📚
Single resource:
return new UserResource($user);
Collection of resources:
return UserResource::collection($users);
The difference:
// Single
{
"data": { "id": 1, "name": "John" }
}
// Collection
{
"data": [
{ "id": 1, "name": "John" },
{ "id": 2, "name": "Jane" }
]
}
Notice the automatic data wrapper? Laravel's got your back!
Custom Wrappers: Make It Your Own 🎁
Don't like the data wrapper? Change it!
class UserResource extends JsonResource
{
public static $wrap = 'user'; // Default is 'data'
}
Or disable it completely:
class UserResource extends JsonResource
{
public static $wrap = null;
}
In AppServiceProvider to change globally:
public function boot()
{
JsonResource::withoutWrapping();
}
Metadata & Links: The Cherry on Top 🍒
Add pagination, links, or custom metadata:
class PostCollection extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => $this->collection,
'links' => [
'self' => route('posts.index'),
],
'meta' => [
'total' => $this->collection->count(),
'version' => '2.0',
],
];
}
}
Returns:
{
"data": [...],
"links": {
"self": "https://api.example.com/posts"
},
"meta": {
"total": 50,
"version": "2.0"
}
}
Bonus: Pivot Data Without the Pain 🔄
Got many-to-many relationships with pivot data? Resources handle it like a boss!
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
// Access pivot data
'role' => $this->whenPivotLoaded('role_user', function () {
return $this->pivot->role_name;
}),
];
}
}
The API Resource Checklist ✅
Before you ship that API endpoint:
- Created a Resource class (not returning raw models)
- Removed sensitive fields (passwords, tokens, internal stuff)
- Used
whenLoaded()for relationships - Used
when()for conditional fields - Formatted dates properly
- Added pagination meta if needed
- Tested with Postman/Insomnia
- Actually checked what JSON you're returning (seriously!)
Real Talk: When NOT to Use Resources 💬
Q: "Should I create a Resource for every model?"
A: Not necessarily! For internal APIs or simple CRUD, raw JSON might be fine. But for public APIs or when you need control? Absolutely use Resources!
Q: "Resources vs Transformers (Fractal)?"
A: Laravel Resources are built-in, easier, and good enough for 95% of use cases. Fractal is more powerful but overkill unless you need advanced features.
Q: "Performance hit?"
A: Minimal! The transformation happens once per request. If you're worried about performance, fix your N+1 queries first!
The Bottom Line
API Resources are like having a bouncer for your database:
- They control what gets out (no password leaks!)
- They format the data (dates, relationships, everything)
- They adapt to context (show different data to different users)
- They keep you sane (consistent responses across your API)
Stop exposing your entire database schema to the internet. Your security team will thank you. Your API consumers will thank you. Heck, future-you will thank you!
The rule: If it's going over HTTP, wrap it in a Resource. No exceptions!
Building APIs? Let's connect on LinkedIn and share war stories! 🚀
Want more Laravel goodness? Star this blog on GitHub and keep learning!
Now go build APIs that don't leak data like a broken faucet! 🔒✨