How to Build a Secure Laravel 11 API with Sanctum – Step‑by‑Step Guide
This tutorial walks you through installing Laravel 11, adding Sanctum for API authentication, creating migrations, models, resources, controllers, and routes, and finally testing the API with Postman, providing complete code snippets and configuration details for a functional backend.
Step 1: Install Laravel 11
Open a terminal and create a new Laravel project.
composer create-project laravel/laravel sanctum-apiNavigate into the project directory.
cd sanctum-apiStep 2: Install Sanctum API
Run the following command to install Sanctum.
php artisan install:apiStep 3: Sanctum Configuration
Add the HasApiTokens trait to app/Models/User.php.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens;
protected $fillable = ['name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}Step 4: Create Blog Migration and Model
Generate a migration and model for a blog resource. php artisan make:model Blog -m The migration file creates a blogs table.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('blogs', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->longText('detail');
$table->timestamps();
});
}
public function down(): void {
Schema::dropIfExists('blogs');
}
};Define the Blog model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
use HasFactory;
protected $fillable = ['title', 'detail'];
}Step 5: Create Eloquent API Resource
Generate a resource class. php artisan make:resource BlogResource Implement the resource to format JSON output.
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class BlogResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'detail' => $this->detail,
'created_at' => $this->created_at->format('d/m/Y'),
'updated_at' => $this->updated_at->format('d/m/Y'),
];
}
}Step 6: Create Controllers
Generate base, register, and blog controllers.
php artisan make:controller API/BaseController
php artisan make:controller API/RegisterController
php artisan make:controller API/BlogControllerBase controller provides unified success and error responses.
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class BaseController extends Controller
{
public function sendResponse($result, $message)
{
return response()->json([
'success' => true,
'data' => $result,
'message' => $message,
], 200);
}
public function sendError($error, $errorMessages = [], $code = 404)
{
$response = ['success' => false, 'message' => $error];
if (!empty($errorMessages)) {
$response['data'] = $errorMessages;
}
return response()->json($response, $code);
}
}Register controller handles user registration and login using Sanctum tokens.
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\API\BaseController;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class RegisterController extends BaseController
{
public function register(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'name' => 'required',
'email' => 'required|email',
'password' => 'required',
'c_password' => 'required|same:password',
]);
if ($validator->fails()) {
return $this->sendError('Validation Error.', $validator->errors());
}
$input = $request->all();
$input['password'] = bcrypt($input['password']);
$user = User::create($input);
$success['token'] = $user->createToken('MyApp')->plainTextToken;
$success['name'] = $user->name;
return $this->sendResponse($success, 'User register successfully.');
}
public function login(Request $request): JsonResponse
{
if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) {
$user = Auth::user();
$success['token'] = $user->createToken('MyApp')->plainTextToken;
$success['name'] = $user->name;
return $this->sendResponse($success, 'User login successfully.');
}
return $this->sendError('Unauthorised.', ['error' => 'Unauthorised']);
}
}Blog controller provides CRUD operations for the blog resource.
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\API\BaseController;
use App\Http\Resources\BlogResource;
use App\Models\Blog;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class BlogController extends BaseController
{
public function index(): JsonResponse
{
$blogs = Blog::all();
return $this->sendResponse(BlogResource::collection($blogs), 'Blogs retrieved successfully.');
}
public function store(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), ['title' => 'required', 'detail' => 'required']);
if ($validator->fails()) {
return $this->sendError('Validation Error.', $validator->errors());
}
$blog = Blog::create($request->all());
return $this->sendResponse(new BlogResource($blog), 'Blog created successfully.');
}
public function show($id): JsonResponse
{
$blog = Blog::find($id);
if (is_null($blog)) {
return $this->sendError('Blog not found.');
}
return $this->sendResponse(new BlogResource($blog), 'Blog retrieved successfully.');
}
public function update(Request $request, Blog $blog): JsonResponse
{
$validator = Validator::make($request->all(), ['title' => 'required', 'detail' => 'required']);
if ($validator->fails()) {
return $this->sendError('Validation Error.', $validator->errors());
}
$blog->update($request->only(['title', 'detail']));
return $this->sendResponse(new BlogResource($blog), 'Blog updated successfully.');
}
public function destroy(Blog $blog): JsonResponse
{
$blog->delete();
return $this->sendResponse([], 'Blog deleted successfully.');
}
}Step 7: Define API Routes
Add authentication and resource routes in routes/api.php.
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\RegisterController;
use App\Http\Controllers\API\BlogController;
Route::controller(RegisterController::class)->group(function(){
Route::post('register', 'register')->name('register');
Route::post('login', 'login')->name('login');
});
Route::middleware('auth:sanctum')->group(function(){
Route::apiResource('blogs', BlogController::class);
Route::get('user', function (Request $request) {
return $request->user();
})->name('user');
});Step 8: Run the Application
Run migrations (configure the database in .env) and start the development server.
php artisan migrate
php artisan serveStep 9: Test the API with Postman
Use the generated routes and include the following headers for authenticated requests:
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
]Below are example Postman screenshots illustrating the request and response flow.
Hope this guide helps you build a secure Laravel 11 API quickly!
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
21CTO
21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
