Updated 15 July 2026
If you’ve ever built a mobile app, a Vue/React frontend, or a third-party integration — you needed a REST API.
Laravel makes building one surprisingly clean. Let me walk you through it from zero.
A REST API lets your Laravel app send and receive data over HTTP — usually JSON.
Instead of returning a view, your controller returns data. The frontend or mobile app handles the display.

|
1 2 3 |
composer create-project laravel/laravel rest-api cd rest-api php artisan serve |
That’s your app running at http://localhost:8000. Now let’s build the API on top of it.
We’ll build a simple Posts API as the example.
|
1 |
php artisan make:model Post -mcr |
The -mcr flag creates the model, migration, and a resource controller in one command.
Open the migration and add the fields:
|
1 2 3 4 5 6 |
Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('body'); $table->timestamps(); }); |
Run it:
|
1 |
php artisan migrate |
Open routes/api.php — this is where all API routes live.
|
1 2 3 |
use App\Http\Controllers\PostController; Route::apiResource('posts', PostController::class); |
One line gives you all 5 routes: index, store, show, update, destroy.
Check them with:
|
1 |
php artisan route:list |
All routes are automatically prefixed with /api/ — so /api/posts, /api/posts/{id}, etc.
Open app/Http/Controllers/PostController.php and fill in the methods:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public function index() { return Post::latest()->paginate(10); } public function store(Request $request) { $data = $request->validate([ 'title' => 'required|string|max:255', 'body' => 'required|string', ]); return Post::create($data); } public function show(Post $post) { return $post; } public function update(Request $request, Post $post) { $post->update($request->validate([ 'title' => 'sometimes|string|max:255', 'body' => 'sometimes|string', ])); return $post; } public function destroy(Post $post) { $post->delete(); return response()->noContent(); } |
Also add fillable to the Post model:
|
1 |
protected $fillable = ['title', 'body']; |
Raw Eloquent output exposes every column. Use API Resources to control what gets returned.
|
1 |
php artisan make:resource PostResource |
|
1 2 3 4 5 6 7 8 9 |
public function toArray($request): array { return [ 'id' => $this->id, 'title' => $this->title, 'body' => $this->body, 'created_at' => $this->created_at->toDateString(), ]; } |
Now wrap your controller responses:
|
1 2 3 4 5 6 7 8 9 10 11 |
use App\Http\Resources\PostResource; public function index() { return PostResource::collection(Post::latest()->paginate(10)); } public function show(Post $post) { return new PostResource($post); } |
Clean, consistent output — no surprise fields leaking to the client.
Sanctum is Laravel’s lightweight token-based auth — perfect for REST APIs.
|
1 2 3 |
composer require laravel/sanctum php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider" php artisan migrate |
Add the HasApiTokens trait to your User model:
|
1 2 3 4 5 6 |
use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; } |
Create a login route that issues a token:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Route::post('/login', function (Request $request) { $request->validate([ 'email' => 'required|email', 'password' => 'required', ]); $user = User::where('email', $request->email)->first(); if (! $user || ! Hash::check($request->password, $user->password)) { return response()->json(['message' => 'Invalid credentials'], 401); } return response()->json([ 'token' => $user->createToken('api-token')->plainTextToken, ]); }); |
Protect your routes with the auth:sanctum middleware:
|
1 2 |
Route::apiResource('posts', PostController::class) ->middleware('auth:sanctum'); |
Now every request needs an Authorization: Bearer {token} header.
For a deeper look at token-based auth, see our guide on Laravel Sanctum API authentication.
By default Laravel returns HTML for errors. For APIs you want JSON.
Add this to app/Exceptions/Handler.php:
|
1 2 3 4 5 6 7 8 9 10 |
public function render($request, Throwable $e) { if ($request->expectsJson()) { return response()->json([ 'message' => $e->getMessage(), ], $this->getHttpCode($e)); } return parent::render($request, $e); } |
Now 404s and validation errors return clean JSON — not an HTML error page.

Route::prefix('v1') from day oneresponse()->noContent() (204) for delete endpoints, not nullFor handling background tasks in your API — like sending emails after a POST — check out our guide on Laravel Queues from zero to production.
routes/api.php and Route::apiResource() for clean route setupThat’s a fully working REST API in Laravel — routes, resources, auth, and error handling.
Start with the Posts example, then replace it with whatever your app actually needs.
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
Be the first to comment.