Building a backend API is a fundamental skill for modern web developers, and Laravel provides an elegant framework to create robust APIs quickly. In this comprehensive guide, we’ll walk through creating a complete Laravel API boilerplate that includes authentication, database migrations, route handling, and proper response formatting.
“Laravel’s elegant syntax and powerful features make it one of the best PHP frameworks for API development, combining simplicity with enterprise-ready capabilities.”
Table of Contents
Prerequisites For Laravel API Development
Before diving into our Laravel API boilerplate, ensure you have the following setup:
- PHP 8.0 or higher installed
- Composer for dependency management
- A database system (MySQL, PostgreSQL, or SQLite)
- Basic understanding of RESTful API concepts
Setting Up Your Laravel Project
Let’s begin by creating a fresh Laravel installation. Open your terminal and run:
composer create-project laravel/laravel api-boilerplate
Navigate to your project directory:
cd api-boilerplate
Configuring Your Environment
Edit the .env
file to set up your database connection:
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=laravel_api DB_USERNAME=root DB_PASSWORD=
Understanding The Laravel Directory Structure
Before we proceed, it’s crucial to understand key directories in our Laravel API project:
- app/Http/Controllers – Houses our API controllers
- routes/api.php – Contains our API routes
- database/migrations – Stores database migration files
- app/Models – Contains our Eloquent models
Creating API Routes
Laravel provides a dedicated routes file for APIs. Open routes/api.php
and let’s define our basic endpoints:
use App\Http\Controllers\API\AuthController; use App\Http\Controllers\API\PostController; Route::prefix('v1')->group(function() { // Authentication routes Route::post('register', [AuthController::class, 'register']); Route::post('login', [AuthController::class, 'login']); // Protected routes Route::middleware('auth:sanctum')->group(function() { Route::post('logout', [AuthController::class, 'logout']); Route::apiResource('posts', PostController::class); }); });
Route Best Practices
When building APIs, follow these routing conventions:
- Use versioning in your API (v1, v2)
- Group related routes together
- Protect sensitive routes with middleware
- Use resource routes where appropriate
Implementing Authentication With Sanctum
Laravel Sanctum provides a lightweight authentication system for SPAs and mobile applications. First, install Sanctum:
composer require laravel/sanctum
Publish the Sanctum configuration files:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Creating The Auth Controller
Generate a new controller for authentication:
php artisan make:controller API/AuthController
Here’s a basic implementation for registration, login, and logout:
namespace App\Http\Controllers\API; use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\ValidationException; class AuthController extends Controller { public function register(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:8|confirmed', ]); $user = User::create([ 'name' => $validated['name'], 'email' => $validated['email'], 'password' => Hash::make($validated['password']), ]); return response()->json([ 'token' => $user->createToken('auth_token')->plainTextToken, 'user' => $user ], 201); } public function login(Request $request) { $request->validate([ 'email' => 'required|email', 'password' => 'required', ]); $user = User::where('email', $request->email)->first(); if (!$user || !Hash::check($request->password, $user->password)) { throw ValidationException::withMessages([ 'email' => ['The provided credentials are incorrect.'], ]); } return response()->json([ 'token' => $user->createToken('auth_token')->plainTextToken, 'user' => $user ]); } public function logout(Request $request) { $request->user()->currentAccessToken()->delete(); return response()->json(['message' => 'Logged out successfully']); } }
Database Migrations And Models
Laravel’s migration system helps version your database schema. Let’s create a migration for a posts table:
php artisan make:migration create_posts_table
Edit the migration file:
public function up() { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->string('title'); $table->text('content'); $table->string('image')->nullable(); $table->timestamps(); }); }
Run the migration:
php artisan migrate
Creating The Post Model
Generate a model for our posts:
php artisan make:model Post
Add fillable fields and relationships:
namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasFactory; protected $fillable = [ 'user_id', 'title', 'content', 'image' ]; public function user() { return $this->belongsTo(User::class); } }
Building The Post Controller
Create a controller for post operations:
php artisan make:controller API/PostController --api
Implement the resource methods:
namespace App\Http\Controllers\API; use App\Http\Controllers\Controller; use App\Models\Post; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; class PostController extends Controller { public function index() { $posts = Post::with('user')->latest()->paginate(10); return response()->json($posts); } public function store(Request $request) { $validated = $request->validate([ 'title' => 'required|string|max:255', 'content' => 'required|string', 'image' => 'nullable|image|max:2048', ]); if ($request->hasFile('image')) { $validated['image'] = $request->file('image')->store('posts'); } $validated['user_id'] = auth()->id(); $post = Post::create($validated); return response()->json($post, 201); } public function show(Post $post) { return response()->json($post->load('user')); } public function update(Request $request, Post $post) { $this->authorize('update', $post); $validated = $request->validate([ 'title' => 'sometimes|string|max:255', 'content' => 'sometimes|string', 'image' => 'sometimes|image|max:2048', ]); if ($request->hasFile('image')) { if ($post->image) { Storage::delete($post->image); } $validated['image'] = $request->file('image')->store('posts'); } $post->update($validated); return response()->json($post); } public function destroy(Post $post) { $this->authorize('delete', $post); if ($post->image) { Storage::delete($post->image); } $post->delete(); return response()->json(null, 204); } }
Handling File Uploads With FormData
When working with APIs, you’ll often need to handle file uploads. Laravel makes this straightforward:
Configuring File Storage
Ensure your .env
has proper storage configuration:
FILESYSTEM_DISK=local
Create a symbolic link to make uploaded files accessible:
php artisan storage:link
Uploading Files Via API
When sending files via API, use FormData in your frontend application. Here’s how Laravel handles it:
// In your controller if ($request->hasFile('image')) { $path = $request->file('image')->store('posts'); // Save $path to your database }
API Response Formatting
Consistent response formatting is crucial for API consumers. Let’s implement a standardized response structure.
Creating A Base API Controller
php artisan make:controller API/BaseController
Add response helper methods:
namespace App\Http\Controllers\API; use App\Http\Controllers\Controller; use Illuminate\Http\JsonResponse; class BaseController extends Controller { public function sendResponse($result, $message = '', $code = 200): JsonResponse { $response = [ 'success' => true, 'data' => $result, 'message' => $message, ]; return response()->json($response, $code); } public function sendError($error, $errorMessages = [], $code = 404): JsonResponse { $response = [ 'success' => false, 'message' => $error, ]; if (!empty($errorMessages)) { $response['data'] = $errorMessages; } return response()->json($response, $code); } }
Using The Response Helpers
Update your controllers to extend BaseController and use these methods:
// Success response return $this->sendResponse($post, 'Post retrieved successfully'); // Error response return $this->sendError('Post not found', [], 404);
Testing Your API
Laravel provides excellent testing capabilities. Let’s create basic API tests.
Creating Feature Tests
php artisan make:test AuthTest
Write test cases for authentication:
namespace Tests\Feature; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class AuthTest extends TestCase { use RefreshDatabase; public function test_user_can_register() { $response = $this->postJson('/api/v1/register', [ 'name' => 'Test User', 'email' => 'test@example.com', 'password' => 'password', 'password_confirmation' => 'password', ]); $response->assertStatus(201) ->assertJsonStructure([ 'token', 'user' => ['id', 'name', 'email'] ]); } public function test_user_can_login() { $user = User::factory()->create([ 'email' => 'test@example.com', 'password' => bcrypt('password'), ]); $response = $this->postJson('/api/v1/login', [ 'email' => 'test@example.com', 'password' => 'password', ]); $response->assertStatus(200) ->assertJsonStructure([ 'token', 'user' => ['id', 'name', 'email'] ]); } }
API Documentation With OpenAPI/Swagger
Documenting your API is essential for developer adoption. Let’s set up Swagger documentation.
Installing L5-Swagger
composer require darkaonline/l5-swagger
Publish the configuration:
php artisan vendor:publish --provider "L5Swagger\L5SwaggerServiceProvider"
Adding API Annotations
Document your endpoints using PHPDoc annotations:
/** * @OA\Post( * path="/api/v1/register", * summary="Register a new user", * @OA\RequestBody( * @OA\MediaType( * mediaType="application/json", * @OA\Schema( * @OA\Property( * property="name", * type="string" * ), * @OA\Property( * property="email", * type="string" * ), * @OA\Property( * property="password", * type="string" * ), * @OA\Property( * property="password_confirmation", * type="string" * ), * example={"name": "Test User", "email": "test@example.com", "password": "password", "password_confirmation": "password"} * ) * ) * ), * @OA\Response( * response=201, * description="User registered successfully", * @OA\JsonContent( * @OA\Property(property="token", type="string"), * @OA\Property(property="user", type="object") * ) * ) * ) */ public function register(Request $request) { ... }
Deploying Your API
When ready to deploy your Laravel API, follow these best practices:
- Set
APP_ENV=production
in your.env
- Generate an application key:
php artisan key:generate
- Optimize performance:
php artisan optimize
- Set up proper file permissions
- Implement HTTPS for security
Conclusion
You’ve now built a comprehensive Laravel API boilerplate with authentication, CRUD operations, file uploads, and proper response formatting. This foundation can be extended for various API projects. Remember to:
- Implement proper validation for all endpoints
- Add rate limiting to prevent abuse
- Write comprehensive tests
- Document your API thoroughly
For more advanced Laravel techniques, check out our guide on Advanced Laravel Techniques or learn about Building Microservices with Laravel.
Be the first to write a comment.