DEV Community

Muhammad Dhiyaul Atha
Muhammad Dhiyaul Atha

Posted on

Membuat API CRUD di Laravel dengan Sanctum (Step by Step)

"Pernah nggak, frontend kamu butuh data dari backend Laravel, tapi API-nya belum aman? Di artikel ini, kamu akan belajar bikin API CRUD yang hanya bisa diakses user terautentikasi pakai Sanctum."


Kenapa Harus Pakai Sanctum untuk API CRUD?

  • Data lebih aman, hanya user login yang bisa akses
  • Cocok untuk SPA, mobile app, dan aplikasi modern
  • Mudah diintegrasikan dengan frontend (React, Vue, dsb)

Studi Kasus: API Artikel (CRUD + Auth)

Kita akan membuat API CRUD Artikel yang hanya bisa diakses user yang sudah login menggunakan Laravel Sanctum.


Step 1: Setup Project Laravel & Sanctum

composer create-project --prefer-dist laravel/laravel belajar-api
cd belajar-api
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Tambahkan middleware Sanctum di app/Http/Kernel.php pada group api:

'api' => [
    \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],
Enter fullscreen mode Exit fullscreen mode

Step 2: Setup Model User & Artikel (Ownership Data)

User

Pastikan model User pakai trait HasApiTokens dan relasi ke artikel:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
    // ...

    public function articles() {
        return $this->hasMany(Article::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

Artikel

php artisan make:model Article -m
Enter fullscreen mode Exit fullscreen mode

Edit migration:

public function up()
{
    Schema::create('articles', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('content');
        $table->foreignId('user_id')->constrained()->cascadeOnDelete();
        $table->timestamps();
    });
}
Enter fullscreen mode Exit fullscreen mode

Jalankan migrasi:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Di model Article.php:

protected $fillable = ['title', 'content', 'user_id'];

public function user() {
    return $this->belongsTo(User::class);
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Auth Controller (Register, Login, Logout)

php artisan make:controller Api/AuthController
Enter fullscreen mode Exit fullscreen mode

Contoh fungsi register, login, logout:

public function register(Request $request) {
    $validated = $request->validate([
        'name' => 'required',
        'email' => 'required|email|unique:users',
        'password' => 'required|min:6',
    ]);
    $user = User::create([
        'name' => $validated['name'],
        'email' => $validated['email'],
        'password' => bcrypt($validated['password']),
    ]);
    $token = $user->createToken('api-token')->plainTextToken;
    return response()->json(['user' => $user, 'token' => $token], 201);
}

public function login(Request $request) {
    $credentials = $request->only('email', 'password');
    if (!auth()->attempt($credentials)) {
        return response()->json(['message' => 'Unauthorized'], 401);
    }
    $user = auth()->user();
    $user->tokens()->delete();
    $token = $user->createToken('api-token')->plainTextToken;
    return response()->json(['user' => $user, 'token' => $token]);
}

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

Step 4: Article Controller (CRUD)

php artisan make:controller Api/ArticleController --resource
Enter fullscreen mode Exit fullscreen mode

Contoh fungsi CRUD:

public function index() {
    return Article::all();
}


public function store(Request $request) {
    $validated = $request->validate([
        'title' => 'required',
        'content' => 'required',
    ]);
    // Ownership: artikel milik user login
    $article = $request->user()->articles()->create($validated);
    return response()->json($article, 201);
}

public function show(Request $request, $id) {
    $article = Article::findOrFail($id);
    // Authorization: hanya pemilik yang boleh akses
    if ($article->user_id !== $request->user()->id) {
        abort(403, 'Unauthorized');
    }
    return $article;
}

public function update(Request $request, $id) {
    $article = Article::findOrFail($id);
    if ($article->user_id !== $request->user()->id) {
        abort(403, 'Unauthorized');
    }
    $validated = $request->validate([
        'title' => 'required',
        'content' => 'required',
    ]);
    $article->update($validated);
    return response()->json($article);
}

public function destroy(Request $request, $id) {
    $article = Article::findOrFail($id);
    if ($article->user_id !== $request->user()->id) {
        abort(403, 'Unauthorized');
    }
    $article->delete();
    return response()->json(null, 204);
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Route API

Edit routes/api.php:

use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\ArticleController;

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

Route::middleware('auth:sanctum')->group(function() {
    Route::post('logout', [AuthController::class, 'logout']);
    Route::apiResource('articles', ArticleController::class);
});
Enter fullscreen mode Exit fullscreen mode

Step 6: Testing API CRUD dengan Postman

Method Endpoint Fungsi
GET /api/articles List semua artikel
POST /api/articles Simpan artikel baru
GET /api/articles/{id} Detail artikel
PUT /api/articles/{id} Update artikel
DELETE /api/articles/{id} Hapus artikel

Jangan lupa kirim token di header:

Authorization: Bearer {token}
Enter fullscreen mode Exit fullscreen mode

Contoh Response JSON

{
    "id": 1,
    "title": "Belajar Laravel API",
    "content": "Laravel API itu powerful...",
    "user_id": 1,
    "created_at": "2026-01-11"
}
Enter fullscreen mode Exit fullscreen mode

Kenapa API Ini Aman?

Dengan Sanctum + auth:sanctum, API ini tidak bisa diakses tanpa token. Setiap request divalidasi berdasarkan user yang login, dan hanya pemilik data yang bisa mengakses/mengubah artikelnya sendiri.


Kalau Mau Dipakai di Frontend

  • React: simpan token di localStorage, kirim di header Authorization setiap request
  • Axios: gunakan interceptor untuk otomatis attach token
  • Jangan expose token di public repo!

Error Handling & Status Code

  • 201 Created: Data berhasil dibuat
  • 200 OK: Data berhasil diambil/diupdate
  • 204 No Content: Data dihapus
  • 401 Unauthorized: Token salah/expired
  • 404 Not Found: Data tidak ditemukan
  • 422 Unprocessable Entity: Validasi gagal

Common Mistake Pemula

  • Lupa migrate/publish Sanctum
  • Lupa middleware auth:sanctum
  • Token tidak dikirim di header
  • Lupa set Accept: application/json
  • Salah endpoint (POST login, bukan GET)
  • Salah group route (harus di dalam middleware)

Next Step

  • API Resource (response lebih rapi)
  • Pagination & filtering
  • Role & permission
  • Testing dengan Thunder Client

🚀 Yuk Diskusi & Share!

Kalau kamu stuck di step tertentu, tulis di komentar ya! Share juga pengalaman kamu bikin API CRUD Laravel, atau request topik lanjutan (API Resource, pagination, dsb).


Referensi:

Top comments (0)