Selamat Datang

Belajar REST API Laravel 12

Modul interaktif ini disusun khusus untuk pemula yang ingin memahami bagaimana membangun backend modern menggunakan Laravel 12. Kita akan membangun API dari nol (zero) hingga menjadi sebuah sistem CRUD yang utuh.

Cara Kerja REST API

REST API bertindak sebagai penghubung antara aplikasi klien (seperti website Vue/React atau aplikasi mobile) dengan database server. API menerima request berupa data JSON melalui protokol HTTP, memproses logika bisnis, dan mengembalikan response yang terstruktur. API memungkinkan satu backend melayani banyak jenis platform secara bersamaan tanpa merusak struktur frontend.

1. Persiapan & Instalasi

Sebelum mulai menulis kode, pastikan peralatan berikut sudah terpasang di komputer Anda:

  • PHP 8.2+ (Saran: Gunakan XAMPP, Herd, atau Laragon)
  • Composer (Package manager untuk PHP)
  • API Client (Gunakan Postman, Insomnia, atau Thunder Client di VS Code)

Membuat Project Baru

Buka terminal/command prompt, navigasikan ke folder tempat Anda ingin menyimpan project, lalu jalankan perintah ini:

composer create-project laravel/laravel api-belajar
cd api-belajar

Menyiapkan Lingkungan API

Mulai Laravel 11 (berlaku di 12), file routing API tidak ada secara *default* untuk membuat aplikasi lebih ringan. Anda harus menginstalnya:

php artisan install:api

Perintah ini akan membuat file routes/api.php dan mengatur konfigurasi dasar API untuk Anda.

2. Routing & Controller

Setiap kali seseorang mengakses URL API kita, Laravel perlu tahu kode mana yang harus dijalankan. Itulah tugas Routing.

Routing Dasar API

Buka file routes/api.php dan tambahkan kode berikut:

use Illuminate\Support\Facades\Route;

Route::get('/hello', function () {
    return response()->json([
        'message' => 'Halo! Ini API pertama saya',
        'status' => 'success'
    ]);
});

Info Prefix API

Setiap route yang ditulis di dalam routes/api.php secara otomatis akan memiliki awalan /api. Jadi URL di atas dapat diakses di http://localhost:8000/api/hello.

3. Database, Model & Migration

Untuk menyimpan data (misalnya data "Produk"), kita perlu menyiapkan database. Secara *default*, Laravel menggunakan database SQLite yang sangat praktis untuk belajar.

Mari buat Model beserta Migration-nya dalam satu perintah:

php artisan make:model Product -m

Buka file migrasi yang baru saja dibuat di database/migrations/xxxx_create_products_table.php dan tambahkan kolom struktur tabel:

public function up(): void
{
    Schema::create('products', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('description')->nullable();
        $table->integer('price');
        $table->timestamps();
    });
}

Eksekusi migrasi ke database:

php artisan migrate

4. CRUD: Menampilkan Data (Read)

Saatnya membuat controller untuk menangani logika CRUD kita.

php artisan make:controller Api/ProductController

Index (Get All Data)

Buka app/Http/Controllers/Api/ProductController.php dan tambahkan method index():

<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Product;

class ProductController extends Controller
{
    public function index()
    {
        // Mengambil semua data produk, diurutkan dari yang terbaru
        $products = Product::latest()->get();

        return response()->json([
            'success' => true,
            'message' => 'Daftar Semua Produk',
            'data'    => $products
        ], 200);
    }
}

Tambahkan route di routes/api.php:

use App\Http\Controllers\Api\ProductController;

GET Route::get('/products', [ProductController::class, 'index']);

Show (Get Single Data)

Untuk menampilkan detail satu produk berdasarkan ID, tambahkan method show():

public function show($id)
{
    // Mencari produk berdasarkan ID
    $product = Product::find($id);

    // Jika produk ditemukan, kembalikan response 200 OK
    if ($product) {
        return response()->json([
            'success' => true,
            'message' => 'Detail Produk',
            'data'    => $product
        ], 200);
    }

    // Jika tidak ditemukan, kembalikan response 404 Not Found
    return response()->json([
        'success' => false,
        'message' => 'Produk Tidak Ditemukan'
    ], 404);
}

Tambahkan route di routes/api.php (jangan lupa untuk memastikan ProductController sudah di-import di atas file):

GET Route::get('/products/{id}', [ProductController::class, 'show']);

5. CRUD: Menyimpan Data (Create)

Bagaimana cara menerima data JSON dari klien dan menyimpannya ke database?

Pertama, izinkan Model untuk diisi. Buka app/Models/Product.php:

class Product extends Model
{
    protected $fillable = ['title', 'description', 'price'];
}

Tambahkan method store() di ProductController.php:

use Illuminate\Http\Request;

public function store(Request $request)
{
    // Menerima input dari request dan menyimpan ke database
    $product = Product::create([
        'title'       => $request->title,
        'description' => $request->description,
        'price'       => $request->price
    ]);

    // Mengembalikan response sukses dengan HTTP Status 201 (Created)
    return response()->json([
        'success' => true,
        'message' => 'Produk Berhasil Ditambahkan',
        'data'    => $product
    ], 201);
}

Tambahkan route di routes/api.php (jangan lupa untuk memastikan ProductController sudah di-import di atas file):

POST Route::post('/products', [ProductController::class, 'store']);

6. Validasi Request

Sebelum menyimpan, kita wajib memastikan data yang dikirim klien sudah benar (misal: title tidak boleh kosong).

Ubah fungsi store() sebelumnya menjadi seperti ini:

use Illuminate\Support\Facades\Validator;

public function store(Request $request)
{
    // 1. Definisikan aturan validasi
    $validator = Validator::make($request->all(), [
        'title' => 'required|string|max:255',
        'price' => 'required|numeric'
    ]);

    // 2. Jika validasi gagal, kembalikan pesan error (422 Unprocessable Entity)
    if ($validator->fails()) {
        return response()->json([
            'success' => false,
            'message' => 'Data tidak valid',
            'errors'  => $validator->errors()
        ], 422);
    }

    // 3. Jika validasi lolos, simpan data ke database
    $product = Product::create($request->all());

    return response()->json([
        'success' => true,
        'message' => 'Produk Berhasil Ditambahkan',
        'data'    => $product
    ], 201);
}

Best Practice

Untuk project skala besar, sangat disarankan menggunakan Form Request (php artisan make:request) agar Controller Anda tetap bersih dan tidak dipenuhi logika validasi.

7. CRUD: Mengubah Data (Update)

Proses update mirip dengan Create, bedanya kita perlu mencari data yang ada terlebih dahulu berdasarkan ID.

Tambahkan method update() di ProductController.php:

public function update(Request $request, $id)
{
    // 1. Validasi input data dari user
    $validator = Validator::make($request->all(), [
        'title' => 'required|string|max:255',
        'price' => 'required|numeric'
    ]);

    if ($validator->fails()) {
        return response()->json([
            'success' => false,
            'message' => 'Data tidak valid',
            'errors'  => $validator->errors()
        ], 422);
    }

    // 2. Cari data yang ingin diubah di database
    $product = Product::find($id);

    if (!$product) {
        return response()->json([
            'success' => false,
            'message' => 'Produk tidak ditemukan'
        ], 404);
    }

    // 3. Lakukan pembaruan data
    $product->update([
        'title'       => $request->title,
        'description' => $request->description,
        'price'       => $request->price
    ]);

    return response()->json([
        'success' => true,
        'message' => 'Produk Berhasil Diperbarui',
        'data'    => $product
    ], 200);
}

Tambahkan route di routes/api.php (jangan lupa untuk memastikan ProductController sudah di-import di atas file):

PUT Route::put('/products/{id}', [ProductController::class, 'update']);

8. CRUD: Menghapus Data (Delete)

Untuk menghapus data, kita cari berdasarkan ID, jika ada kita hapus (delete()).

Tambahkan method destroy() di ProductController.php:

public function destroy($id)
{
    // 1. Cari data yang akan dihapus
    $product = Product::find($id);

    if (!$product) {
        return response()->json([
            'success' => false,
            'message' => 'Produk tidak ditemukan'
        ], 404);
    }

    // 2. Hapus data dari database
    $product->delete();

    // 3. Kembalikan response sukses
    return response()->json([
        'success' => true,
        'message' => 'Produk Berhasil Dihapus'
    ], 200);
}

Tambahkan route di routes/api.php (jangan lupa untuk memastikan ProductController sudah di-import di atas file):

DELETE Route::delete('/products/{id}', [ProductController::class, 'destroy']);

9. API Resources (Transformasi Data)

Saat kita membuat API berskala produksi, mengembalikan data mentah langsung dari Model (database) ke pengguna adalah praktik yang buruk. Mengapa?

  • Keamanan: Anda bisa saja secara tidak sengaja membocorkan data sensitif (misalnya password, status *banned*).
  • Konsistensi: Jika nama kolom database berubah (misal dari `created_at` menjadi `tanggal_dibuat`), aplikasi klien (Frontend) Anda akan rusak karena menanti struktur data yang lama.

Solusi: Gunakan Laravel API Resources untuk menjadi "penerjemah" antara Database dan struktur JSON yang final.

Membuat Resource Class

Jalankan perintah Artisan ini untuk membuat class Resource khusus untuk tabel Product:

php artisan make:resource ProductResource

Buka file app/Http/Resources/ProductResource.php dan ubah isinya untuk menentukan kolom apa saja yang boleh dilihat oleh publik:

<?php
namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ProductResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        // Mendefinisikan struktur JSON yang akan dikembalikan
        return [
            'id'           => $this->id,
            'nama_produk'  => strtoupper($this->title), // Contoh: mengubah ke huruf kapital
            'deskripsi'    => $this->description,
            'harga'        => 'Rp ' . number_format($this->price, 0, ',', '.'), // Contoh format uang
            'tanggal_buat' => $this->created_at->format('d-m-Y H:i'),
        ];
    }
}

Keunggulan Resource

Perhatikan bahwa kita mengubah nama key title menjadi nama_produk dan memformat nilai harga menjadi bentuk Rupiah langsung dari Backend. Frontend kini hanya perlu menampilkan datanya apa adanya!

Menerapkan Resource di Controller

Sekarang, mari ubah method show() dan index() pada ProductController agar menggunakan Resource yang baru kita buat.

Ubah method show() (Read Single Data):

use App\Http\Resources\ProductResource;

public function show($id)
{
    $product = Product::find($id);

    if ($product) {
        // Menggunakan ProductResource untuk satu buah data
        return response()->json([
            'success' => true,
            'message' => 'Detail Produk',
            'data'    => new ProductResource($product)
        ], 200);
    }

    return response()->json([
        'success' => false,
        'message' => 'Produk Tidak Ditemukan'
    ], 404);
}

Ubah method index() (Read All Data):

public function index()
{
    $products = Product::latest()->get();

    // Menggunakan ProductResource::collection untuk sekumpulan data (Array)
    return response()->json([
        'success' => true,
        'message' => 'Daftar Semua Produk',
        'data'    => ProductResource::collection($products)
    ], 200);
}

10. Lanjutan: Otentikasi dengan Sanctum

Saat ini, siapa pun di internet bisa menambah, mengubah, atau menghapus produk di API kita karena sifatnya masih publik. Untuk mengamankannya, kita perlu memastikan hanya *user* yang sudah *login* (memiliki token) yang boleh melakukan modifikasi (Create, Update, Delete).

Laravel menyediakan paket ringan yang sangat tangguh bernama Laravel Sanctum.

1. Instalasi dan Setup Sanctum

Jika Anda membuat project dengan install:api di awal (Laravel 11+), Sanctum biasanya sudah ikut diinstal. Jika belum, atau untuk memastikannya, jalankan:

php artisan install:api

Kemudian buka model User (app/Models/User.php) dan pastikan *trait* HasApiTokens sudah digunakan:

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable; // <- Pastikan HasApiTokens ada di sini

    // ...
}

2. Membuat AuthController

Kita butuh fungsi untuk registrasi (membuat akun), login (mendapatkan token), dan logout (menghapus token). Buat controller baru:

php artisan make:controller Api/AuthController

Isi app/Http/Controllers/Api/AuthController.php dengan logika pendaftaran dan pembuatan token:

<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

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

        if ($validator->fails()) {
            return response()->json($validator->errors(), 422);
        }

        // 1. Buat User Baru
        $user = User::create([
            'name'     => $request->name,
            'email'    => $request->email,
            'password' => Hash::make($request->password)
        ]);

        // 2. Ciptakan Token untuk User ini
        $token = $user->createToken('auth_token')->plainTextToken;

        // 3. Kembalikan data user beserta token-nya
        return response()->json([
            'message'      => 'Registrasi berhasil',
            'access_token' => $token,
            'token_type'   => 'Bearer'
        ], 201);
    }

    public function login(Request $request)
    {
        // Cek kecocokan email dan password
        $user = User::where('email', $request->email)->first();

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

        // Ciptakan Token Baru
        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json([
            'message'      => 'Login berhasil',
            'access_token' => $token,
            'token_type'   => 'Bearer'
        ], 200);
    }

    public function logout(Request $request)
    {
        // Hapus token yang sedang digunakan untuk sesi ini
        $request->user()->currentAccessToken()->delete();

        return response()->json([
            'message' => 'Berhasil logout'
        ], 200);
    }
}

3. Mengunci Route API

Langkah terakhir adalah mendaftarkan *route* Otentikasi dan mengunci *route* Produk kita. Buka routes/api.php:

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

// Route Publik (Bisa diakses siapa saja tanpa Login)
POST Route::post('/register', [AuthController::class, 'register']);
POST Route::post('/login', [AuthController::class, 'login']);

GET  Route::get('/products', [ProductController::class, 'index']);
GET  Route::get('/products/{id}', [ProductController::class, 'show']);

// Route Privat (Wajib pakai Token Sanctum)
Route::middleware('auth:sanctum')->group(function () {

    POST   Route::post('/logout', [AuthController::class, 'logout']);

    POST   Route::post('/products', [ProductController::class, 'store']);
    PUT    Route::put('/products/{id}', [ProductController::class, 'update']);
    DELETE Route::delete('/products/{id}', [ProductController::class, 'destroy']);

});

Cara Menggunakan Token

Setelah Login, Anda akan mendapat access_token. Salin token tersebut. Saat mengakses rute privat, atur di tab Authorization Postman menjadi Bearer Token, lalu *paste* token-nya. Jika lupa menyertakan token, Laravel otomatis menolak request dengan pesan Unauthenticated.

11. Testing API dengan Postman / Thunder Client

Setelah seluruh fitur selesai dibuat, tahapan krusial berikutnya adalah melakukan pengujian (*testing*). Kita tidak menggunakan browser biasa untuk mengetes API, melainkan aplikasi seperti Postman atau Thunder Client.

Mengatur Environment

Daripada mengetik ulang http://localhost:8000/api berulang kali, buatlah Environment Variable di Postman:

  • Buat variable {{base_url}} dengan nilai http://localhost:8000/api.
  • Buat variable {{token}} (biarkan kosong dulu, nanti diisi setelah login).

Mengetes Alur Lengkap (Flow)

Uji API Anda dengan urutan logis berikut untuk memastikan tidak ada celah logika:

  1. Register: Kirim POST ke {{base_url}}/register dengan body JSON berisi name, email, dan password.
  2. Login: Kirim POST ke {{base_url}}/login. Salin access_token yang didapat ke variabel {{token}}.
  3. Akses Ditolak: Coba kirim POST ke {{base_url}}/products tanpa mengatur Bearer token. Pastikan Anda mendapat error `401 Unauthenticated`.
  4. Create: Pasang Bearer Token, kirim POST ke {{base_url}}/products dengan body title dan price. Pastikan mendapat `201 Created`.
  5. Read & Update: Lakukan GET untuk melihat data masuk, lalu PUT untuk mengubah namanya.
  6. Delete: Hapus data tersebut, lalu coba GET ulang ID tersebut. Pastikan mendapat `404 Not Found`.

12. Deployment (Mengonlinekan API)

API yang hanya hidup di localhost tidak bisa digunakan oleh aplikasi mobile atau frontend *production*. Anda harus mendeploy-nya ke server nyata.

Pilihan Hosting Backend

  • PaaS (Platform as a Service): Railway, Render, atau Heroku. Sangat mudah, cukup hubungkan dengan GitHub, namun harganya bisa mahal saat skala membesar.
  • VPS (Virtual Private Server): DigitalOcean, Vultr, atau AWS EC2. Jauh lebih murah, namun Anda harus mengatur Linux, Nginx, dan PHP secara manual.
  • Serverless (Bawaan Laravel): Laravel Vapor (berjalan di atas AWS Lambda), pilihan standar industri untuk Laravel skala besar.

Checklist Pra-Deployment

Sebelum *push* kode Anda ke server produksi, wajib perhatikan hal berikut:

Keamanan & Performa

  • Ubah APP_ENV=local menjadi APP_ENV=production di file .env server.
  • Ubah APP_DEBUG=true menjadi APP_DEBUG=false. Jika tetap `true`, struktur database dan *stack trace* kode Anda akan bocor saat terjadi error.
  • Jalankan php artisan config:cache dan php artisan route:cache di server agar performa API melesat lebih cepat.
  • Pastikan koneksi database di server sudah diatur (umumnya menggunakan MySQL/PostgreSQL, hindari SQLite di production jika trafiknya tinggi).