laravel-api
from juststeveking/laravel-api-skill
A comprehensive Claude skill for building production-grade Laravel REST APIs with clean architecture, type safety, and Laravel best practices.
npx skills add https://github.com/juststeveking/laravel-api-skill --skill laravel-apiSKILL.md
Laravel API - Steve's Architecture
Build Laravel REST APIs with clean, stateless, resource-scoped architecture.
Quick Start
When user requests a Laravel API, follow this workflow:
- Understand requirements - What resources? What operations? Authentication needed?
- Initialize project structure - Set up routing, remove frontend bloat
- Build first resource - Complete CRUD to establish pattern
- Add authentication - JWT via PHP Open Source Saver
- Iterate on remaining resources - Follow established pattern
Core Architecture Principles
Read references/architecture.md for comprehensive details. Key principles:
- Stateless by design - No hidden dependencies, explicit data flow
- Boundary-first - Clear separation of HTTP, business logic, data layers
- Resource-scoped - Routes, controllers organized by resource
- Version discipline - Namespace-based versioning, HTTP Sunset headers
Code Quality Standards
All code must follow Laravel best practices and PSR-12 standards:
- Preserve Functionality - Refactorings change HOW code works, never WHAT it does
- Explicit Over Implicit - Prefer clear, readable code over clever shortcuts
- Type Declarations - Always use return types on methods, parameter types where beneficial
- Avoid Nested Ternaries - Use match expressions, switch, or if/else for clarity
- Consistent Naming - Follow PSR-12 and Laravel conventions strictly
- Proper Namespacing - Organize imports logically, use full type hints
When reviewing or refactoring code:
- Focus on clarity and maintainability over cleverness
- Simplify complex nested logic into readable structures
- Extract magic values into named constants or config
- Remove unnecessary complexity while preserving exact behavior
Project Structure
routes/api/
routes.php # Main entry point, version grouping
tasks.php # All task routes, all versions
projects.php # All project routes, all versions
app/Http/
Controllers/{Resource}/V1/
StoreController.php # Always invokable
IndexController.php
ShowController.php
Requests/{Resource}/V1/
StoreTaskRequest.php # Validation + payload() method
Payloads/{Resource}/
StoreTaskPayload.php # Simple DTOs with toArray()
Responses/
JsonDataResponse.php # Implements Responsable
JsonErrorResponse.php
Middleware/
HttpSunset.php
app/Actions/{Resource}/
CreateTask.php # Single-purpose business logic
app/Services/ # Only when logic too complex for Actions
app/Models/
Task.php # HasUlids trait, simple data access
Building a New Resource Endpoint
Step 1: Model
Always use ULIDs. Keep models simple - data access only.
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class Task extends Model
{
use HasFactory;
use HasUlids;
protected $fillable = [
'title',
'description',
'status',
'project_id',
];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
}
Step 2: Routes
Create resource route file at routes/api/{resource}.php:
use App\Http\Controllers\Tasks\V1;
Route::middleware(['auth:api'])->group(function () {
Route::get('/tasks', V1\IndexController::class);
Route::post('/tasks', V1\StoreController::class);
Route::get('/tasks/{task}', V1\ShowController::class);
Route::patch('/tasks/{task}', V1\UpdateController::class);
Route::delete('/tasks/{task}', V1\DestroyController::class);
});
Include in routes/api/routes.php:
Route::prefix('v1')->group(function () {
require __DIR__ . '/tasks.php';
});
Step 3: DTO (Payload)
Create at app/Http/Payloads/{Resource}/{Operation}Payload.php:
<?php
declare(strict_types=1);
namespace App\Http\Payloads\Tasks;
final readonly class StoreTaskPayload
{
public function __construct(
public string $title,
public ?string $description,
public string $status,
public string $projectId,
) {}
public function toArray(): array
{
return [
'title' => $this->title,
'description' => $this->description,
'status' => $this->status,
'project_id' => $this->projectId,
];
}
}
Step 4: Form Request
Create at app/Http/Requests/{Resource}/V1/{Operation}Request.php:
<?php
declare(strict_types=1);
namespace App\Http\Requests\Tasks\V1;
use App\Http\Payloads\Tasks\StoreTaskPayload;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\V
...