Whether you're building a mobile backend, a microservice, or a headless CMS, APIs are the backbone of modern software. Laravel provides one of the most elegant frameworks for building them. In this guide, I'll walk you through every step of building a production-ready RESTful API — from project scaffolding to authentication, versioning, testing, and deployment.
This isn't a surface-level overview. We'll cover the patterns and practices I use in real-world projects that serve thousands of requests per minute.
Why Laravel for APIs?
Laravel stands out for API development because of its first-class support for:
- Eloquent ORM with eager loading and query scopes for efficient data access
- API Resources for consistent, transformable JSON responses
- Form Requests for clean, reusable validation logic
- Sanctum for lightweight token-based authentication
- Built-in rate limiting to protect your endpoints
- Comprehensive testing tools with PHPUnit and Pest
Other frameworks can do these things, but Laravel bundles them into a cohesive, well-documented ecosystem.
Setting Up the Project
Start by creating a fresh Laravel project. I recommend the latest stable release:
composer create-project laravel/laravel api-project
cd api-project
Next, configure your .env file for your database:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=api_project
DB_USERNAME=root
DB_PASSWORD=
php artisan migrate
Designing Your Database Schema
Good API design starts with a solid data model. Let's build a blog API with posts, categories, and tags:
php artisan make:model Post -mf
php artisan make:model Category -mf
php artisan make:model Tag -mf
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('category_id')->nullable()->constrained()->nullOnDelete();
$table->string('title');
$table->string('slug')->unique();
$table->text('excerpt')->nullable();
$table->longText('content');
$table->enum('status', ['draft', 'published', 'archived'])->default('draft');
$table->timestamp('published_at')->nullable();
$table->timestamps();
});
Building Resource Controllers
Laravel's resource controllers map RESTful actions to controller methods automatically:
php artisan make:controller Api/PostController --api --model=Post
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\PostResource;
use App\Models\Post;
class PostController extends Controller
{
public function index()
{
$posts = Post::with(['user', 'category', 'tags'])
->published()
->latest()
->paginate(15);
return PostResource::collection($posts);
}
public function store(StorePostRequest $request): PostResource
{
$post = Post::create($request->validated());
return new PostResource($post->load(['user', 'category', 'tags']));
}
public function show(Post $post): PostResource
{
return new PostResource($post->load(['user', 'category', 'tags']));
}
public function destroy(Post $post)
{
$post->delete();
return response()->json(null, 204);
}
}
Authentication with Sanctum
Laravel Sanctum provides lightweight authentication for SPAs and token-based APIs:
php artisan install:api
// routes/api.php
Route::post('/login', [AuthController::class, 'login']);
// Public routes
Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/{post}', [PostController::class, 'show']);
// Protected routes
Route::middleware('auth:sanctum')->group(function () {
Route::post('/posts', [PostController::class, 'store']);
Route::put('/posts/{post}', [PostController::class, 'update']);
Route::delete('/posts/{post}', [PostController::class, 'destroy']);
});
Always use HTTPS in production when using token-based authentication. Tokens sent over HTTP can be intercepted.
Rate Limiting
// app/Providers/AppServiceProvider.php
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
Testing Your API
it('lists published posts', function () {
Post::factory()->published()->count(5)->create();
$response = $this->getJson('/api/posts');
$response->assertOk()
->assertJsonCount(5, 'data');
});
it('requires authentication to create a post', function () {
$this->postJson('/api/posts', ['title' => 'Test'])
->assertUnauthorized();
});
Conclusion
Building a RESTful API with Laravel is a rewarding experience. The framework provides everything you need out of the box.
Key takeaways:
- Use API Resources to control your response format
- Use Form Requests to keep validation clean and reusable
- Use Sanctum for simple, secure token-based auth
- Rate limit your endpoints to prevent abuse
- Write tests — they catch bugs before your users do
Start with these patterns, and you'll have a solid foundation for any API project.
Comments