Reader Stacks

Building a Simple REST API in Laravel

API routes, resource controllers, and API Resources for shaping JSON responses — the minimum real structure for a Laravel API, not just returning models directly.

Building a Simple REST API in Laravel

A minimal but real Laravel API needs three pieces working together: routes under routes/api.php, a resource controller, and an API Resource class to control exactly what JSON shape gets returned — skipping that last piece and returning Eloquent models directly is the most common shortcut that causes problems later.

1. Routes

// routes/api.php
Route::apiResource('posts', PostController::class);

apiResource() registers the standard index/store/show/update/destroy routes, skipping the create/edit form routes a normal web resource controller would also register — those don't make sense for a JSON API.

2. The controller

php artisan make:controller Api/PostController --api
public function index()
{
    return PostResource::collection(Post::latest()->paginate(20));
}

public function show(Post $post)
{
    return new PostResource($post);
}

public function store(StorePostRequest $request)
{
    $post = Post::create($request->validated());

    return new PostResource($post);
}

3. Why an API Resource, not the model directly

Returning Post::all() directly leaks every column — including ones you never meant to expose (internal flags, foreign keys with no meaning to a client). An API Resource makes the response shape explicit and independent of the database schema:

php artisan make:resource PostResource
public function toArray($request): array
{
    return [
        'id' => $this->id,
        'title' => $this->title,
        'excerpt' => $this->excerpt,
        'published_at' => $this->published_at?->toIso8601String(),
        'author' => $this->whenLoaded('author', fn () => [
            'name' => $this->author->name,
        ]),
    ];
}

whenLoaded() only includes the relationship if it was actually eager-loaded — protecting against an accidental N+1 query triggered by the resource itself when a relationship wasn't loaded.

Validation belongs in a Form Request

Keep validation out of the controller with a dedicated request class (php artisan make:request StorePostRequest) — it keeps the controller focused on orchestration and makes the validation rules independently testable.

Topics: APIs & Integrations