KelasIT.com
Web Development

Membuat REST API dengan Laravel

Bangun REST API Laravel dari nol: route API, controller, validasi, API Resource, kode status HTTP yang tepat, penanganan error, hingga pengujian dengan curl dan autentikasi dasar Sanctum.

Membuat REST API dengan Laravel

REST API adalah pintu data yang dipakai aplikasi mobile, frontend JavaScript, atau sistem lain. Laravel menyediakan semua yang dibutuhkan untuk membuatnya dengan rapi. Di tutorial ini kita membuat API untuk resource Produk dengan operasi lengkap.

Belum paham konsep REST? Baca dulu Belajar REST API dari Dasar.

1. Persiapan model dan tabel

php artisan make:model Produk -m

Migration:

Schema::create('produks', function (Blueprint $table) {
    $table->id();
    $table->string('nama');
    $table->decimal('harga', 12, 2);
    $table->unsignedInteger('stok')->default(0);
    $table->timestamps();
});

Model:

class Produk extends Model
{
    protected $fillable = ['nama', 'harga', 'stok'];
}

Jalankan php artisan migrate.

2. Mendefinisikan route API

Route API ditulis di routes/api.php dan otomatis diberi awalan /api serta dijaga middleware api (tanpa session/CSRF dan dengan pembatasan laju).

use App\Http\Controllers\Api\ProdukController;

Route::apiResource('produk', ProdukController::class);

apiResource membuat lima route tanpa create/edit (karena API tidak punya form HTML):

MethodURLAksi
GET/api/produkindex: daftar
POST/api/produkstore: buat
GET/api/produk/{id}show: detail
PUT/PATCH/api/produk/{id}update: ubah
DELETE/api/produk/{id}destroy: hapus

3. API Resource: bentuk respons yang konsisten

Jangan mengembalikan model mentah karena kamu bisa tidak sengaja membocorkan kolom internal. Gunakan API Resource untuk mengontrol bentuk JSON:

php artisan make:resource ProdukResource
class ProdukResource extends JsonResource
{
    public function toArray($request): array
    {
        return [
            'id'    => $this->id,
            'nama'  => $this->nama,
            'harga' => (float) $this->harga,
            'stok'  => $this->stok,
            'dibuat'=> $this->created_at->toIso8601String(),
        ];
    }
}

4. Controller

php artisan make:controller Api/ProdukController --api --model=Produk
class ProdukController extends Controller
{
    public function index()
    {
        return ProdukResource::collection(
            Produk::latest()->paginate(15)
        );
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'nama'  => 'required|string|max:255',
            'harga' => 'required|numeric|min:0',
            'stok'  => 'integer|min:0',
        ]);

        $produk = Produk::create($data);

        return (new ProdukResource($produk))
            ->response()
            ->setStatusCode(201);
    }

    public function show(Produk $produk)
    {
        return new ProdukResource($produk);
    }

    public function update(Request $request, Produk $produk)
    {
        $data = $request->validate([
            'nama'  => 'sometimes|required|string|max:255',
            'harga' => 'sometimes|required|numeric|min:0',
            'stok'  => 'sometimes|integer|min:0',
        ]);

        $produk->update($data);

        return new ProdukResource($produk);
    }

    public function destroy(Produk $produk)
    {
        $produk->delete();

        return response()->noContent();   // 204
    }
}

Detail penting:

  • paginate(15) otomatis menambahkan links dan meta (halaman, total) pada respons.
  • Validasi yang gagal pada permintaan JSON otomatis menghasilkan respons 422 berisi daftar errors, tanpa kode tambahan, asalkan klien mengirim header Accept: application/json.
  • Route model binding mengembalikan 404 otomatis jika ID tidak ditemukan.
  • sometimes berarti "validasi hanya jika field dikirim", cocok untuk update parsial.

5. Kode status yang benar

SituasiKode
Berhasil membaca / mengubah200 OK
Berhasil membuat201 Created
Berhasil menghapus (tanpa isi)204 No Content
Input tidak valid422 Unprocessable Entity
Tidak login401 Unauthorized
Login tetapi tidak berhak403 Forbidden
Tidak ditemukan404 Not Found
Terlalu banyak permintaan429 Too Many Requests

6. Menguji dengan curl

# Buat produk
curl -X POST http://localhost:8000/api/produk \
  -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{"nama":"Pensil","harga":2500,"stok":100}'

# Daftar
curl -H "Accept: application/json" http://localhost:8000/api/produk

# Ubah sebagian
curl -X PATCH http://localhost:8000/api/produk/1 \
  -H "Accept: application/json" -H "Content-Type: application/json" \
  -d '{"stok":80}'

# Hapus
curl -X DELETE -H "Accept: application/json" http://localhost:8000/api/produk/1

Alat grafis seperti Postman, Insomnia, atau Bruno juga nyaman dipakai.

7. Mengamankan dengan Sanctum

Tanpa autentikasi, siapa pun bisa menghapus datamu. Sanctum menyediakan token sederhana untuk API:

Route::apiResource('produk', ProdukController::class)
    ->middleware('auth:sanctum');

Setelah login, buat token: $user->createToken('mobile')->plainTextToken. Klien mengirimnya di header Authorization: Bearer <token>. Selalu gunakan HTTPS agar token tidak bisa disadap.

8. Kesalahan umum

  • Lupa header Accept: application/json, sehingga error tampil sebagai redirect/HTML.
  • Mengembalikan 200 untuk semua hal termasuk error. Gunakan kode status yang tepat.
  • Tidak memakai $fillable, sehingga error mass assignment atau lebih buruk, kolom sensitif bisa diubah klien.
  • Tidak membatasi laju (throttle) pada endpoint login.
  • Mengirim data sensitif (password hash, token) dalam respons.

Menangani relasi dan filter pada API

Tambahkan relasi pada resource dengan whenLoaded agar tidak memicu query tambahan yang tidak perlu:

'kategori' => new KategoriResource($this->whenLoaded('kategori')),

Klien dapat meminta relasi lewat query string yang kamu whitelist:

$produk = Produk::query()
    ->when($request->boolean('dengan_kategori'), fn ($q) => $q->with('kategori'))
    ->when($request->kategori_id, fn ($q, $id) => $q->where('kategori_id', $id))
    ->paginate(15);

Format respons list yang dihasilkan Laravel

Pada ProdukResource::collection($paginator), bentuk JSON-nya:

{
  "data": [ { "id": 1, "nama": "Pensil", "harga": 2500, "stok": 100 } ],
  "links": { "first": "...?page=1", "next": "...?page=2", "prev": null },
  "meta": { "current_page": 1, "per_page": 15, "total": 42, "last_page": 3 }
}

Klien tinggal membaca meta.total dan links.next untuk membuat navigasi halaman.

Menulis test untuk API

public function test_membuat_produk_gagal_jika_harga_negatif(): void
{
    $this->actingAs(User::factory()->create(), 'sanctum')
        ->postJson('/api/produk', ['nama' => 'X', 'harga' => -5])
        ->assertStatus(422)
        ->assertJsonValidationErrors(['harga']);
}

public function test_menghapus_mengembalikan_204(): void
{
    $produk = Produk::factory()->create();

    $this->actingAs(User::factory()->create(), 'sanctum')
        ->deleteJson("/api/produk/{$produk->id}")
        ->assertNoContent();

    $this->assertDatabaseMissing('produks', ['id' => $produk->id]);
}

Jalankan dengan php artisan test. Test otomatis memberi keberanian untuk mengubah kode tanpa takut merusak perilaku yang sudah benar.

CORS: saat frontend berbeda domain

Jika frontend (misalnya app.contoh.com) memanggil API di api.contoh.com, browser memblokir permintaan kecuali server mengizinkan lewat CORS. Atur config/cors.php:

'paths' => ['api/*'],
'allowed_origins' => ['https://app.contoh.com'],
'allowed_methods' => ['*'],
'allowed_headers' => ['*'],

Jangan mengisi allowed_origins dengan * pada API yang memakai kredensial. Sebutkan domain yang dipercaya saja.

Daftar tugas sebelum API dipakai klien

  • [ ] Semua endpoint memakai kode status yang benar.
  • [ ] Validasi dan pesan error konsisten.
  • [ ] Autentikasi dan pembatasan laju terpasang.
  • [ ] Paginasi pada semua daftar.
  • [ ] Tidak ada data sensitif di respons.
  • [ ] Dokumentasi contoh request/response tersedia.
  • [ ] Test untuk jalur sukses dan gagal.

Kesimpulan

Pola REST API di Laravel: route apiResource → validasi → API Resource → kode status yang tepat → autentikasi. Setelah paham pola ini, menambah resource baru hanya soal mengulang langkah yang sama. Untuk praktik terbaik yang lebih lanjut seperti versioning dan format error, lihat Tips Membuat API dengan Laravel.

H
Hifzon

Menulis tentang pemrograman, database, AI, dan karier di dunia IT supaya belajar terasa lebih mudah dan menyenangkan.

Komentar (0)

Komentar hanya untuk member. Masuk atau daftar gratis untuk ikut berdiskusi.

Mau belajar lebih dalam?

Course online dengan latihan coding interaktif dan sertifikat.

Lihat Course →

Gabung komunitas

Tanya jawab, info event, dan teman belajar di grup WhatsApp.

Gabung Grup