API design significantly impacts application performance and developer experience. Laravel API Resources and GraphQL offer different approaches with distinct tradeoffs. At ZIRA Software, we choose based on project requirements rather than trends.
Quick Comparison
Laravel API Resources (REST) GraphQL
├── Multiple endpoints ├── Single endpoint
├── Server-defined response ├── Client-defined response
├── HTTP caching ├── Complex caching
├── Simple tooling ├── Rich tooling
├── Predictable performance ├── Variable performance
└── Lower learning curve └── Higher learning curve
Laravel API Resources
// app/Http/Resources/UserResource.php
class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'avatar' => $this->avatar_url,
'posts_count' => $this->whenCounted('posts'),
'posts' => PostResource::collection($this->whenLoaded('posts')),
'created_at' => $this->created_at->toISOString(),
];
}
}
// Controller
class UserController extends Controller
{
public function show(User $user)
{
return new UserResource(
$user->loadCount('posts')->load('posts.comments')
);
}
public function index(Request $request)
{
$users = User::query()
->when($request->with_posts, fn($q) => $q->with('posts'))
->paginate();
return UserResource::collection($users);
}
}
GraphQL with Laravel
// Using Lighthouse PHP
// graphql/schema.graphql
type Query {
user(id: ID! @eq): User @find
users(
name: String @where(operator: "like")
orderBy: _ @orderBy(columns: ["created_at", "name"])
): [User!]! @paginate
}
type User {
id: ID!
name: String!
email: String!
posts: [Post!]! @hasMany
postsCount: Int! @count(relation: "posts")
createdAt: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User! @belongsTo
comments: [Comment!]! @hasMany
}
# Client query - fetch exactly what's needed
query GetUserWithPosts($id: ID!) {
user(id: $id) {
id
name
posts {
id
title
commentsCount
}
}
}
When to Choose REST/API Resources
Best for:
// 1. Simple CRUD operations
Route::apiResource('posts', PostController::class);
// 2. Public APIs with caching
public function index()
{
return Cache::remember('posts.index', 3600, function () {
return PostResource::collection(Post::published()->get());
});
}
// 3. File uploads
public function store(Request $request)
{
$path = $request->file('document')->store('documents');
// REST handles file uploads naturally
}
// 4. Webhooks and integrations
// Third-party services expect REST endpoints
Route::post('/webhooks/stripe', [WebhookController::class, 'stripe']);
Strengths:
- HTTP caching (ETags, Last-Modified)
- Simpler monitoring and debugging
- Standard HTTP status codes
- Lower server overhead
- Easier rate limiting
When to Choose GraphQL
Best for:
# 1. Mobile apps with varying data needs
query MobileHome {
currentUser {
id
name
avatar
}
notifications(first: 5) {
id
message
createdAt
}
}
# 2. Complex nested data
query Dashboard {
organization {
name
teams {
name
members {
name
role
recentActivity(first: 3) {
action
timestamp
}
}
}
stats {
totalUsers
activeProjects
}
}
}
# 3. Real-time subscriptions
subscription OnMessageReceived($channelId: ID!) {
messageReceived(channelId: $channelId) {
id
content
sender {
name
avatar
}
}
}
Strengths:
- Single request for complex data
- Client controls response shape
- Strong typing and introspection
- Excellent developer tooling
- Subscription support
Performance Considerations
// REST - Predictable N+1 prevention
class PostResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
// Only included if already loaded
'author' => new UserResource($this->whenLoaded('author')),
];
}
}
// Controller handles loading
public function index()
{
return PostResource::collection(
Post::with('author')->paginate()
);
}
// GraphQL - Must handle N+1 carefully
// Use DataLoader pattern or Lighthouse's @with directive
type Post {
author: User! @belongsTo
# Lighthouse automatically batches with DataLoader
}
Hybrid Approach
// Use both where appropriate
// routes/api.php
// REST for simple CRUD
Route::apiResource('posts', PostController::class);
Route::post('/upload', [UploadController::class, 'store']);
// GraphQL for complex queries
Route::post('/graphql', GraphQLController::class);
// Example: REST for writes, GraphQL for reads
Decision Matrix
| Factor | REST | GraphQL | |--------|------|---------| | Mobile apps | Good | Better | | Public API | Better | Good | | Caching | Better | Complex | | File uploads | Better | Complex | | Complex queries | Multiple requests | Single request | | Learning curve | Lower | Higher | | Tooling | Standard | Excellent | | Bandwidth | Over-fetching | Optimized |
Conclusion
Laravel API Resources excel for simple APIs, public endpoints, and file operations. GraphQL shines for complex data requirements, mobile apps, and real-time features. Choose based on your specific needs, not trends.
Need API architecture guidance? Contact ZIRA Software for API design consultation.