Introduction

A REST API rarely needs the create and edit methods that serve HTML forms. Route::apiResource is a variant of Route::resource that drops those methods and gives you a clean, five-route RESTful API.

Key Concepts

  • Route::apiResource: Registers only index, store, show, update, and destroy.
  • --api flag: Generates a controller with just those five methods.
  • Route::apiResources([...]): Register multiple API resources in a single array.
  • middlewareFor(): Apply middleware to specific methods of a resource (Laravel 13 API).

Real World Context

Every mobile app and SPA backend is shaped like this: five endpoints per resource, responding with JSON. Using apiResource keeps your routes file short and consistent so developers can navigate a new service in seconds.

Deep Dive

Generating the Controller

bash
php artisan make:controller Api/PostController --api --model=Post

The --api flag skips the form methods; --model=Post pre-fills the parameter types for route model binding.

Registering the Routes

php
// routes/api.php
use App\Http\Controllers\Api\PostController;

Route::apiResource('posts', PostController::class);

// Multiple at once
Route::apiResources([
    'posts'    => PostController::class,
    'comments' => CommentController::class,
]);

This generates five routes:

VerbURIActionName
GET/api/postsindexposts.index
POST/api/postsstoreposts.store
GET/api/posts/{post}showposts.show
PUT/PATCH/api/posts/{post}updateposts.update
DELETE/api/posts/{post}destroyposts.destroy

Protecting Specific Methods

Laravel 13 introduced middlewareFor() so you can attach middleware to just the methods that need it:

php
Route::apiResource('posts', PostController::class)
    ->middlewareFor(['store', 'update', 'destroy'], 'auth:sanctum');

index and show remain public; mutations require a valid token.

A Real Controller

php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\StorePostRequest;
use App\Http\Resources\PostResource;
use App\Models\Post;

class PostController extends Controller
{
    public function index()
    {
        return PostResource::collection(Post::latest()->paginate(20));
    }

    public function store(StorePostRequest $request)
    {
        $post = $request->user()->posts()->create($request->validated());
        return new PostResource($post);
    }

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

    public function destroy(Post $post)
    {
        $post->delete();
        return response()->noContent();  // HTTP 204
    }
}

PostResource is an Eloquent API Resource that shapes the JSON response. response()->noContent() returns a 204 with an empty body — the correct response for a successful DELETE.

Common Pitfalls

  1. Using Route::resource for an API — The extra create and edit routes clutter your route list and advertise HTML-only endpoints that do not exist.
  2. Returning models directly — It works, but it exposes every field. Wrap responses in API Resources so the shape is explicit and stable.

Best Practices

  1. Name your controllers under App\Http\Controllers\Api — Keeps web and API controllers separate so they can evolve independently.
  2. Use middlewareFor for mixed public/private APIs — It is cleaner than splitting the resource into individual routes.

Summary

  • Route::apiResource registers 5 RESTful routes (no create/edit).
  • Use --api with make:controller to generate matching controller methods.
  • Laravel 13's middlewareFor() targets specific resource actions.
  • Wrap responses in API Resources and return 204 No Content from destructive actions.

Code Examples

php
// routes/api.php
use App\Http\Controllers\Api\PostController;

Route::apiResource('posts', PostController::class)
    ->middlewareFor(['store', 'update', 'destroy'], 'auth:sanctum');

// This yields:
// GET    /api/posts            (public)
// GET    /api/posts/{post}     (public)
// POST   /api/posts            (auth:sanctum)
// PUT    /api/posts/{post}     (auth:sanctum)
// DELETE /api/posts/{post}     (auth:sanctum)
✓ Completed