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.

21CTO
21CTO
21CTO
How to Build a Secure Laravel 11 API with Sanctum – Step‑by‑Step Guide

Step 1: Install Laravel 11

Open a terminal and create a new Laravel project.

composer create-project laravel/laravel sanctum-api

Navigate into the project directory.

cd sanctum-api

Step 2: Install Sanctum API

Run the following command to install Sanctum.

php artisan install:api

Step 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/BlogController

Base 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 serve

Step 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!

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backend-developmentAuthenticationPHPAPILaravelSanctum
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.