api-resource-patterns

Best practices for Laravel API Resources including resource transformation, collection handling, conditional attributes, and relationship loading.

16 stars

Best use case

api-resource-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Best practices for Laravel API Resources including resource transformation, collection handling, conditional attributes, and relationship loading.

Teams using api-resource-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/api-resource-patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/backend/api-resource-patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/api-resource-patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How api-resource-patterns Compares

Feature / Agentapi-resource-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Best practices for Laravel API Resources including resource transformation, collection handling, conditional attributes, and relationship loading.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# API Resource Patterns

## Basic Resource Structure

```php
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class PostResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'content' => $this->content,
            'created_at' => $this->created_at->toISOString(),
            'updated_at' => $this->updated_at->toISOString(),
        ];
    }
}
```

## Conditional Attributes

```php
public function toArray($request): array
{
    return [
        'id' => $this->id,
        'title' => $this->title,
        
        // Only include if loaded
        'author' => new UserResource($this->whenLoaded('user')),
        
        // Only include if condition is true
        'content' => $this->when($request->user()?->can('view', $this->resource), $this->content),
        
        // Only include if not null
        'comments_count' => $this->when($this->comments_count !== null, $this->comments_count),
        
        // Merge conditionally
        $this->mergeWhen($request->user()?->isAdmin(), [
            'internal_notes' => $this->internal_notes,
        ]),
    ];
}
```

## Nested Relationships

```php
public function toArray($request): array
{
    return [
        'id' => $this->id,
        'title' => $this->title,
        
        // Single relationship
        'author' => new UserResource($this->whenLoaded('user')),
        
        // Collection relationship
        'comments' => CommentResource::collection($this->whenLoaded('comments')),
        
        // Nested relationships
        'category' => new CategoryResource($this->whenLoaded('category')),
    ];
}
```

## Resource Collections

```php
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\ResourceCollection;

class PostCollection extends ResourceCollection
{
    public function toArray($request): array
    {
        return [
            'data' => $this->collection,
            'meta' => [
                'total' => $this->total(),
                'count' => $this->count(),
                'per_page' => $this->perPage(),
                'current_page' => $this->currentPage(),
                'total_pages' => $this->lastPage(),
            ],
            'links' => [
                'self' => $request->url(),
                'first' => $this->url(1),
                'last' => $this->url($this->lastPage()),
                'prev' => $this->previousPageUrl(),
                'next' => $this->nextPageUrl(),
            ],
        ];
    }
}
```

## Adding Links

```php
public function toArray($request): array
{
    return [
        'id' => $this->id,
        'title' => $this->title,
        'links' => [
            'self' => route('posts.show', $this->id),
            'author' => route('users.show', $this->user_id),
            'comments' => route('posts.comments.index', $this->id),
        ],
    ];
}
```

## Resource Response Customization

```php
// In controller
public function store(Request $request)
{
    $post = Post::create($request->validated());
    
    return (new PostResource($post))
        ->response()
        ->setStatusCode(201)
        ->header('Location', route('posts.show', $post));
}
```

## Pivot Data in Resources

```php
public function toArray($request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'assigned_at' => $this->whenPivotLoaded('role_user', function () {
            return $this->pivot->created_at;
        }),
        'expires_at' => $this->whenPivotLoadedAs('assignment', 'role_user', function () {
            return $this->assignment->expires_at;
        }),
    ];
}
```

## Wrapping and Unwrapping

```php
// Disable wrapping in AppServiceProvider
use Illuminate\Http\Resources\Json\JsonResource;

public function boot()
{
    JsonResource::withoutWrapping();
}

// Or per resource
public static $wrap = 'post';
```

## With Additional Data

```php
public function with($request): array
{
    return [
        'version' => '1.0.0',
        'timestamp' => now()->toISOString(),
    ];
}

public function withResponse($request, $response)
{
    $response->header('X-Value', 'True');
}
```

## Best Practices

### Always Use whenLoaded for Relationships

```php
// ✅ Prevents N+1 queries
'author' => new UserResource($this->whenLoaded('user')),

// ❌ Will cause N+1 queries
'author' => new UserResource($this->user),
```

### Use Type Hints

```php
use Illuminate\Http\Request;

public function toArray(Request $request): array
{
    // ...
}
```

### Keep Resources Focused

```php
// ✅ Create separate resources for different contexts
class PostResource extends JsonResource { }
class PostListResource extends JsonResource { }
class PostDetailResource extends JsonResource { }

// ❌ Don't make one resource do everything
```

### Use Resource Collections

```php
// ✅ Use collection class
return new PostCollection(Post::paginate());

// ✅ Or collection method
return PostResource::collection(Post::all());
```

## Controller Usage

```php
class PostController extends Controller
{
    public function index()
    {
        $posts = Post::with(['user', 'category'])
            ->withCount('comments')
            ->paginate(15);
        
        return new PostCollection($posts);
    }
    
    public function show(Post $post)
    {
        $post->load(['user', 'comments.user', 'tags']);
        
        return new PostResource($post);
    }
    
    public function store(StorePostRequest $request)
    {
        $post = Post::create($request->validated());
        
        return (new PostResource($post))
            ->response()
            ->setStatusCode(201);
    }
}
```

## Checklist

- [ ] Resources transform models consistently
- [ ] Relationships loaded with whenLoaded()
- [ ] Conditional attributes use when()
- [ ] Collections include pagination metadata
- [ ] Links included for HATEOAS
- [ ] Type hints used
- [ ] Proper HTTP status codes
- [ ] No N+1 queries
- [ ] Consistent date formatting
- [ ] Appropriate wrapping strategy

Related Skills

memory-safety-patterns

16
from diegosouzapw/awesome-omni-skill

Implement memory-safe programming with RAII, ownership, smart pointers, and resource management across Rust, C++, and C. Use when writing safe systems code, managing resources, or preventing memory...

llm-app-patterns

16
from diegosouzapw/awesome-omni-skill

Production-ready patterns for building LLM applications. Covers RAG pipelines, agent architectures, prompt IDEs, and LLMOps monitoring. Use when designing AI applications, implementing RAG, building agents, or setting up LLM observability.

dbt-transformation-patterns

16
from diegosouzapw/awesome-omni-skill

Master dbt (data build tool) for analytics engineering with model organization, testing, documentation, and incremental strategies. Use when building data transformations, creating data models, or ...

data-fetching-patterns

16
from diegosouzapw/awesome-omni-skill

Explains data fetching strategies including fetch on render, fetch then render, render as you fetch, and server-side data fetching. Use when implementing data loading, optimizing loading performance, or choosing between client and server data fetching.

airflow-dag-patterns

16
from diegosouzapw/awesome-omni-skill

Build production Apache Airflow DAGs with best practices for operators, sensors, testing, and deployment. Use when creating data pipelines, orchestrating workflows, or scheduling batch jobs.

ai-product-patterns

16
from diegosouzapw/awesome-omni-skill

Builds AI-native products using OpenAI's development philosophy and modern AI UX patterns. Use when integrating AI features, designing for model improvements, implementing evals as product specs, or creating AI-first experiences. Based on Kevin Weil (OpenAI CPO) on building for future models, hybrid approaches, and cost optimization.

a2a-executor-patterns

16
from diegosouzapw/awesome-omni-skill

Agent-to-Agent (A2A) executor implementation patterns for task handling, execution management, and agent coordination. Use when building A2A executors, implementing task handlers, creating agent execution flows, or when user mentions A2A protocol, task execution, agent executors, task handlers, or agent coordination.

GitOps Patterns

16
from diegosouzapw/awesome-omni-skill

ArgoCD ApplicationSets, progressive delivery, Harness GitX, and multi-cluster GitOps patterns

dotnet-gha-patterns

16
from diegosouzapw/awesome-omni-skill

Composes GitHub Actions workflows. Reusable workflows, composite actions, matrix, caching.

bats-testing-patterns

16
from diegosouzapw/awesome-omni-skill

Comprehensive guide for writing shell script tests using Bats (Bash Automated Testing System). Use when writing or improving tests for Bash/shell scripts, creating test fixtures, mocking commands, or setting up CI/CD for shell script testing. Includes patterns for assertions, setup/teardown, mocking, fixtures, and integration with GitHub Actions.

bash-defensive-patterns

16
from diegosouzapw/awesome-omni-skill

Master defensive Bash programming techniques for production-grade scripts. Use when writing robust shell scripts, CI/CD pipelines, or system utilities requiring fault tolerance and safety.

apollo-client-patterns

16
from diegosouzapw/awesome-omni-skill

Use when implementing Apollo Client patterns for queries, mutations, cache management, and local state in React applications.