Membuat REST API CRUD dengan Laravel serta JWT

| | | Laravel
Membuat REST API CRUD dengan Laravel serta JWT

Di kesempatan kali kita akan membahas cara membuat REST API Laravel versi 11 dengan sistem CRUD (Create, Read, Update, Delete) disertai autentikasi JWT memakai Laravel Sanctum. dulu saya pernah menulis versi lama pakai Laravel 8 + Passport, tapi sekarang buat API sederhana saya lebih milih Sanctum karena lebih ringan dan jadi rekomendasi resmi. API ini bisa dipakai sebagai backend untuk Android Studio, Vue Js frontend, dan web service lainnya.

Fitur yang akan kita bangun mencakup Login, Logout, Register, Update, dan Delete dengan studi kasus simple user. API sebagai backend bisa mengubah profile seperti update name, email, dan password disertai enkripsi bcrypt.

Install Laravel versi terbaru

di tulisan ini saya pakai Laravel 11.x

composer create-project laravel/laravel rest-api-laravel

Penjelasan :

  • membuat project laravel terbaru di PC kita dengan bantuan composer

Install Laravel Sanctum

composer require laravel/sanctum

Penjelasan :

  • Sanctum adalah library autentikasi yang dikembangkan Laravel. dengan Sanctum kita mendapatkan token untuk mengakses API, dan setiap request ke endpoint yang dilindungi harus disertai header Authorization : Bearer {TOKEN}

setelah composer selesai, jalankan migrasi supaya tabel personal_access_tokens ikut dibuat:

php artisan migrate

Menyiapkan Database di Laravel

Jangan lupa setup dulu koneksi database, di sini saya pakai MySQL dengan nama database rest-api-laravel. isi file .env seperti berikut:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=rest-api-laravel
DB_USERNAME=root
DB_PASSWORD=

Penjelasan :

  • DB_CONNECTION : database yang kita gunakan, di sini MySQL
  • DB_HOST : alamat host mysql
  • DB_PORT : port yang digunakan oleh mysql
  • DB_DATABASE : nama database mysql yang akan digunakan
  • DB_USERNAME : username untuk mengakses database mysql
  • DB_PASSWORD : password yang digunakan oleh mysql

setingan .env disesuaikan dengan konfigurasi mysql teman-teman. buat yang kesulitan membuat database di mysql bisa kunjungi tutorial saya di cara membuat database mysql

Menyiapkan Model User dan Middleware

setelah Sanctum terinstall, tambahkan trait HasApiTokens di model User:

<?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 HasApiTokens, HasFactory, Notifiable;
}

Penjelasan :

  • trait HasApiTokens dari Sanctum membuat model User punya method createToken() dan tokens(), jadi kita bisa menerbitkan dan mencabut token untuk user

lalu di routes/api.php, route yang butuh login dibungkus middleware auth:sanctum:

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;

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

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', [UserController::class, 'profile']);
    Route::patch('/user', [UserController::class, 'patch']);
    Route::put('/user', [UserController::class, 'put']);
    Route::delete('/user', [UserController::class, 'delete']);
    Route::post('/user/logout', [UserController::class, 'logout']);
});

Penjelasan :

  • Route::middleware('auth:sanctum') melindungi semua route di dalamnya. request tanpa header token yang valid akan ditolak dengan status 401

Membuat Repository User

di project ini saya pakai pola Repository, jadi akses database dipisah dari controller. buat folder app/Repositories lalu file UserRepository.php:

<?php

namespace App\Repositories;

use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

class UserRepository
{
    public function create(array $input)
    {
        return User::create([
            'name'     => $input['name'],
            'email'    => $input['email'],
            'password' => Hash::make($input['password']),
        ]);
    }

    public function update(array $input, User $user)
    {
        if (isset($input['password'])) {
            $input['password'] = Hash::make($input['password']);
        }

        $user->update($input);
        return $user->fresh();
    }

    public function delete(User $user)
    {
        $user->delete();
        return $user;
    }
}

Penjelasan :

  • Hash::make untuk mengenkripsi password, pengganti fungsi bcrypt() yang lama
  • method create, update, delete terpisah biar controller tetap ramping

Membuat Controller User

buat controller UserController.php yang memanggil repository dan menerbitkan token Sanctum:

<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class UserController extends Controller
{
    protected $repo;

    public function __construct(UserRepository $repo)
    {
        $this->repo = $repo;
    }

    public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name'     => 'required|string',
            'email'    => 'required|email|unique:users,email',
            'password' => 'required|string|min:8',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status'  => false,
                'message' => $validator->messages()->first(),
            ], 400);
        }

        $user  = $this->repo->create($request->all());
        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json([
            'status' => true,
            'message' => 'Berhasil Membuat User baru',
            'user'   => $user,
            'token'  => $token,
        ], 201);
    }

    public function login(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'email'    => 'required|email|exists:users,email',
            'password' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'status'  => false,
                'message' => $validator->messages()->first(),
            ], 400);
        }

        $user = User::where('email', $request->email)->first();

        if (!$user || !Hash::check($request->password, $user->password)) {
            return response()->json([
                'status'  => false,
                'message' => 'Email atau password salah',
            ], 401);
        }

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

        return response()->json([
            'status' => true,
            'message' => 'Login berhasil',
            'user'   => $user,
            'token'  => $token,
        ], 200);
    }

    public function profile(Request $request)
    {
        return response()->json($request->user(), 200);
    }

    public function put(Request $request)
    {
        return $this->updateProfile($request, false);
    }

    public function patch(Request $request)
    {
        return $this->updateProfile($request, true);
    }

    protected function updateProfile(Request $request, bool $partial)
    {
        $rules = [
            'name'  => ($partial ? 'sometimes|' : '') . 'required|string',
            'email' => ($partial ? 'sometimes|' : '') . 'required|email|unique:users,email,' . $request->user()->id,
            'password' => 'sometimes|string|min:8',
        ];

        $validator = Validator::make($request->all(), $rules);
        if ($validator->fails()) {
            return response()->json([
                'status'  => false,
                'message' => $validator->messages()->first(),
            ], 400);
        }

        $user = $this->repo->update($request->all(), $request->user());

        return response()->json([
            'status' => true,
            'message' => 'Berhasil Update user',
            'user'   => $user,
        ], 200);
    }

    public function delete(Request $request)
    {
        $this->repo->delete($request->user());

        return response()->json([
            'status'  => true,
            'message' => 'Berhasil Delete user',
        ], 200);
    }

    public function logout(Request $request)
    {
        $request->user()->currentAccessToken()->delete();

        return response()->json([
            'message' => 'Successfully logged out',
        ], 200);
    }
}

Penjelasan :

  • $user->createToken('auth_token')->plainTextToken; : menerbitkan token Sanctum, plainTextToken yang mengambil string token-nya
  • Hash::check untuk memverifikasi password saat login
  • $request->user()->currentAccessToken()->delete(); : menghapus token yang sedang dipakai, sehingga logout

Mencoba Endpoint dengan cURL atau Postman

jalankan server laravel:

php artisan serve

lalu tes dengan cURL. contoh register:

curl -X POST http://127.0.0.1:8000/api/register \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"name":"Sigit","email":"[email protected]","password":"rahasia123"}'

response-nya akan berisi token. pakai token itu untuk akses route yang dilindungi:

curl -X GET http://127.0.0.1:8000/api/user \
  -H "Accept: application/json" \
  -H "Authorization: Bearer TOKEN_ANDA"

kalau token valid, response mengembalikan data user. kalau tidak, muncul error 401 Unauthenticated. dulu pas pertama kali coba saya sering ketinggalan header Accept: application/json, hasilnya malah dilempar ke halaman login HTML, bingung sendiri 😄

Pertanyaan yang Sering Diajukan

Laravel Sanctum itu apa?

Sanctum adalah paket autentikasi resmi Laravel untuk API. Dia menerbitkan token personal access token (string acak) yang disimpan di tabel personal_access_tokens, dan cukup ringan karena tidak butuh OAuth server seperti Passport.

Kenapa sekarang pakai Sanctum bukan Passport untuk API?

Untuk API sederhana yang cuma butuh token login, Sanctum adalah rekomendasi resmi Laravel. Passport masih dipakai kalau butuh OAuth penuh seperti login dengan aplikasi pihak ketiga (login via GitHub, Google, dll).

Cara mengirim token Sanctum saat request API?

Tambahkan header Authorization: Bearer <token> di setiap request ke route yang dilindungi middleware auth:sanctum. Token didapat dari hasil login atau register.

Berapa lama token Sanctum berlaku?

Secara default token Sanctum tidak kedaluwarsa. Kalau mau ada masa berlaku, set expiresAt() saat membuat token, atau pakai abilities untuk membatasi akses token tertentu.

Sigit Nurhanafi avatar
Full-stack developer & technical writer. Berpengalaman di PHP, Laravel, NodeJS, MySQL, dan Python. Aktif menulis tutorial pemrograman dan maintaining open-source projects di PemburuKode sejak 2021.