Kritim Yantra
Mar 24, 2025
Laravel remains one of the most popular PHP frameworks for building robust APIs, and with Laravel 12, the process has become even more streamlined. In this guide, we'll walk through the essential steps to build a modern API with Laravel 12, covering everything from setup to deployment.
Before we dive in, let's understand why Laravel is an excellent choice for API development:
First, create a new Laravel project:
composer create-project laravel/laravel laravel-api
cd laravel-api
Install API:
php artisan install:api
Laravel separates web and API routes. All API routes go in routes/api.php
:
use App\Http\Controllers\AuthController;
use App\Http\Controllers\PostController;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('posts', PostController::class);
Route::post('/logout', [AuthController::class, 'logout']);
});
Notice how we're using auth:sanctum
middleware to protect our routes.
Let's create a Post
model with migration:
php artisan make:model Post -m
Edit the migration file:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('title');
$table->text('content');
$table->timestamps();
});
}
Run the migration:
php artisan migrate
Create an AuthController:
php artisan make:controller AuthController
Implement the authentication methods:
namespace App\Http\Controllers;
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)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8',
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
return response()->json([
'token' => $user->createToken('api-token')->plainTextToken,
], 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('api-token')->plainTextToken,
]);
}
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => 'Logged out']);
}
}
Generate a controller for our posts:
php artisan make:controller PostController --api
Implement the CRUD operations:
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
return Post::where('user_id', auth()->id())->get();
}
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
]);
$post = Post::create([
'user_id' => auth()->id(),
'title' => $validated['title'],
'content' => $validated['content'],
]);
return response()->json($post, 201);
}
public function show(Post $post)
{
$this->authorize('view', $post);
return $post;
}
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
$validated = $request->validate([
'title' => 'sometimes|string|max:255',
'content' => 'sometimes|string',
]);
$post->update($validated);
return $post;
}
public function destroy(Post $post)
{
$this->authorize('delete', $post);
$post->delete();
return response()->noContent();
}
}
Create a policy for the Post model:
php artisan make:policy PostPolicy --model=Post
Define the authorization rules in app/Policies/PostPolicy.php
:
public function view(User $user, Post $post)
{
return $user->id === $post->user_id;
}
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
public function delete(User $user, Post $post)
{
return $user->id === $post->user_id;
}
Don't forget to register the policy in app/Providers/AuthServiceProvider.php
:
protected $policies = [
Post::class => PostPolicy::class,
];
For consistent API responses, create a trait in app/Traits/ApiResponse.php
:
namespace App\Traits;
trait ApiResponse
{
protected function success($data = null, $message = null, $code = 200)
{
return response()->json([
'success' => true,
'message' => $message,
'data' => $data,
], $code);
}
protected function error($message = null, $code = 400, $errors = null)
{
return response()->json([
'success' => false,
'message' => $message,
'errors' => $errors,
], $code);
}
}
Now you can use this trait in your controllers for consistent responses.
Laravel provides built-in rate limiting. Configure it in app/Http/Kernel.php
:
protected $middlewareGroups = [
'api' => [
'throttle:api',
// ...
],
];
Then configure the rates in routes/api.php
:
Route::middleware(['auth:sanctum', 'throttle:60,1'])->group(function () {
// Your protected routes here
});
For API documentation, install L5-Swagger:
composer require darkaonline/l5-swagger
php artisan vendor:publish --provider "L5Swagger\L5SwaggerServiceProvider"
Add annotations to your controllers:
/**
* @OA\Post(
* path="/api/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"
* ),
* example={"name": "John Doe", "email": "john@example.com", "password": "secret"}
* )
* )
* ),
* @OA\Response(
* response=201,
* description="User registered successfully"
* ),
* @OA\Response(
* response=422,
* description="Validation error"
* )
* )
*/
Generate the documentation:
php artisan l5-swagger:generate
Laravel provides excellent testing tools. Create a test:
php artisan make:test PostApiTest
Write some tests:
public function test_user_can_create_post()
{
$user = User::factory()->create();
$token = $user->createToken('test-token')->plainTextToken;
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $token,
])->postJson('/api/posts', [
'title' => 'Test Post',
'content' => 'This is a test post content',
]);
$response->assertStatus(201)
->assertJson([
'title' => 'Test Post',
'content' => 'This is a test post content',
]);
}
Run the tests:
php artisan test
When deploying your Laravel API:
APP_ENV=production
in your .env
filephp artisan key:generate
php artisan optimize
config/cors.php
/api/v1/posts
Building APIs with Laravel 12 is a straightforward process thanks to the framework's built-in features and excellent ecosystem. By following this guide, you've learned how to:
Remember that API development is an iterative process. As your application grows, you'll need to consider additional aspects like:
Laravel provides all the tools you need to build professional-grade APIs that can scale with your application's needs. Happy coding!
No comments yet. Be the first to comment!
Please log in to post a comment:
Continue with Google