Building REST APIs with Laravel Sanctum

Building a REST API is an essential skill once your application needs to be consumed by a mobile app, a React/Vue single-page application (SPA), or another service. Laravel Sanctum...

Building REST APIs with Laravel Sanctum

Building a REST API is an essential skill once your application needs to be consumed by a mobile app, a React/Vue single-page application (SPA), or another service. Laravel Sanctum provides a lightweight yet secure token-based authentication system without the complexity of OAuth. This tutorial builds a complete API with register and login endpoints, protected routes, and clean JSON using API Resources.

1. Installing Laravel Sanctum

On Laravel 11 the API scaffolding can be installed with a single artisan command that also wires up Sanctum, the routes/api.php file, and the token migration:

php artisan install:api

This adds the laravel/sanctum package, publishes the personal_access_tokens table migration, and then you just run the migration:

php artisan migrate

For older versions (Laravel 10), install manually:

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

2. Preparing the User Model

So the User can issue tokens, add the HasApiTokens trait to the App\Models\User model:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

The trait provides a createToken() method that generates a random token for the client to store and send with every request.

3. Register and Login Endpoints

Create a dedicated authentication controller:

php artisan make:controller Api/AuthController

Fill the register() method to create a new user and issue a token at once:

public function register(Request $request)
{
    $data = $request->validate([
        'name'     => 'required|string|max:255',
        'email'    => 'required|email|unique:users',
        'password' => 'required|min:8|confirmed',
    ]);

    $user = User::create([
        'name'     => $data['name'],
        'email'    => $data['email'],
        'password' => Hash::make($data['password']),
    ]);

    $token = $user->createToken('api-token')->plainTextToken;

    return response()->json([
        'user'  => $user,
        'token' => $token,
    ], 201);
}

The login() method checks the credentials then returns a fresh token. Always use the same error message for a wrong email or password so you never leak which one was wrong:

public function login(Request $request)
{
    $data = $request->validate([
        'email'    => 'required|email',
        'password' => 'required',
    ]);

    $user = User::where('email', $data['email'])->first();

    if (! $user || ! Hash::check($data['password'], $user->password)) {
        return response()->json([
            'message' => 'Invalid credentials.',
        ], 401);
    }

    $token = $user->createToken('api-token')->plainTextToken;

    return response()->json(['token' => $token]);
}

4. Registering API Routes

All API routes live in routes/api.php and automatically get the /api prefix. Public routes for register/login, and a protected group for the rest:

use App\Http\Controllers\Api\AuthController;

Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', fn (Request $request) => $request->user());
    Route::post('/logout', [AuthController::class, 'logout']);
    Route::apiResource('products', ProductController::class);
});

5. Protecting Routes with auth:sanctum

The auth:sanctum middleware inspects the Authorization: Bearer <token> header. If the token is invalid, Laravel automatically rejects it with a 401 status. Inside a protected route, $request->user() returns the user who owns the token.

For logout, delete the token currently in use so it can no longer be used:

public function logout(Request $request)
{
    $request->user()->currentAccessToken()->delete();
    return response()->json(['message' => 'Logged out successfully.']);
}

6. Shaping JSON with API Resources

Returning a raw model leaks columns you may not want exposed. An API Resource gives you full control over the JSON shape:

php artisan make:resource ProductResource
public function toArray(Request $request): array
{
    return [
        'id'    => $this->id,
        'name'  => $this->name,
        'price' => (int) $this->price,
        'created_at' => $this->created_at->toDateString(),
    ];
}

Use it in the controller. For collections use ::collection():

public function index()
{
    return ProductResource::collection(Product::latest()->paginate(10));
}

public function show(Product $product)
{
    return new ProductResource($product);
}

7. Testing with cURL and Postman

Test register from the terminal:

curl -X POST http://localhost:8000/api/register \
  -H "Accept: application/json" \
  -d "name=Budi&email=budi@mail.com&password=secret88&password_confirmation=secret88"

Grab the token from the response, then hit a protected route:

curl http://localhost:8000/api/user \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 1|xxxxxxxxxxxxxxxxx"

In Postman, open the Authorization tab, choose the Bearer Token type, and paste the token. Always send the Accept: application/json header so validation errors come back as JSON instead of an HTML page.

Best Practices

  • Always use HTTPS in production so tokens cannot be intercepted.
  • Give tokens meaningful names (e.g. per device) so you can revoke them individually.
  • Limit token abilities with scopes: createToken('name', ['product:read']) then check with tokenCan().
  • Apply rate limiting to the login endpoint to prevent brute-force attacks.

With this pattern you have a secure, well-structured REST API: register and login issue tokens, auth:sanctum guards your routes, and API Resources ensure consistent JSON ready to be consumed by mobile apps or modern frontends.

Yudhi
Written by
Yudhi
Founder & Lead Developer, GudangCode

Yudhi is the founder of GudangCode and a Laravel developer who has built dozens of ready-to-use business information systems — from POS and HRIS to management apps. He writes guides and articles on GudangCode to help Indonesian developers run, understand, and deploy Laravel source code correctly.

LaravelPHPMySQLSistem Informasi Bisnis See all articles by Yudhi
Want the full source code & apps?

Sign up free to download ready-to-use business applications, information systems, and Laravel source code.

Sign Up Free & Download
Laravel Sanctum Api Programming & Laravel
Share this article
Back to Blog
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →

Get Full Access Now!

Join our membership and unlock exclusive access to all premium features. Fast, easy, and ready to use instantly.

Join Membership Now
Tim Support
Online
Isi data dulu untuk mulai chat:
Beri rating & testimoni sebelum menutup:
Live chat by gudangcode.com