โ† Semua pembelajaran / Laravel Nol โ†’ Enterprise
Fase 5 ยท API & Frontend

API Resource

Mengembalikan model langsung terasa praktis sampai suatu hari sebuah kolom internal ikut terkirim, atau sebuah rename memutus aplikasi klien. Resource membuat bentuk keluaran jadi keputusan yang ditulis.

Sumber asli laravel.com Resmi Rangkuman ~7 menit baca

Intisari

  • Resource adalah kelas yang menerjemahkan satu model jadi array โ€” kamu yang menentukan isinya.
  • ResourceCollection untuk daftar; paginasi otomatis menghasilkan blok links dan meta.
  • whenLoaded() mencegah resource memicu query N+1 diam-diam.
  • Laravel 13 menyertakan JSON:API resource bawaan โ€” lengkap dengan sparse fieldset dan relasi.
  • Skema database boleh berubah; bentuk API tidak boleh berubah tanpa versi baru.

Masalah yang dipecahkan

// Tanpa resource โ€” bentuk API = bentuk tabel
return Artikel::latest()->paginate(20);

Enam bulan kemudian, seseorang menambahkan kolom catatan_internal dan skor_moderasi. Keduanya langsung terkirim ke setiap klien API โ€” tidak ada satu pun kode yang berubah di controller, dan tidak ada satu pun tes yang gagal.

php artisan make:resource ArtikelResource
class ArtikelResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id'          => $this->id,
            'judul'       => $this->judul,
            'slug'        => $this->slug,
            'ringkasan'   => $this->ringkasan,
            'terbit_pada' => $this->terbit_pada?->toIso8601String(),

            // Hanya kalau relasinya sudah di-eager load โ€” tidak memicu query baru
            'penulis'  => PenulisResource::make($this->whenLoaded('penulis')),
            'kategori' => KategoriResource::make($this->whenLoaded('kategori')),

            // Hanya kalau sudah dihitung
            'jumlah_komentar' => $this->whenCounted('komentar'),

            // Hanya untuk pengguna tertentu
            'catatan_internal' => $this->when(
                $request->user()?->can('moderasi', $this->resource),
                fn () => $this->catatan_internal,
            ),
        ];
    }
}
// Satu
return ArtikelResource::make($artikel);

// Banyak, dengan paginasi โ€” links dan meta ditambahkan otomatis
return ArtikelResource::collection(
    Artikel::with('penulis', 'kategori')->withCount('komentar')->paginate(20)
);

whenLoaded() bukan sekadar kerapian. Kalau kamu menulis 'penulis' => PenulisResource::make($this->penulis) tanpa whenLoaded, resource itu akan memuat relasi per baris โ€” N+1 yang sempurna, tersembunyi di lapisan yang jarang dicurigai. Dengan whenLoaded, field-nya cuma dihilangkan dari keluaran kalau relasinya belum dimuat, dan masalahnya jadi terlihat sebagai field yang hilang, bukan sebagai halaman yang lambat.

Bentuk respons yang dihasilkan

{
  "data": [
    { "id": 1, "judul": "โ€ฆ", "penulis": { "id": 4, "nama": "Ani" } }
  ],
  "links": { "first": "โ€ฆ?page=1", "last": "โ€ฆ?page=9", "prev": null, "next": "โ€ฆ?page=2" },
  "meta":  { "current_page": 1, "per_page": 20, "total": 178 }
}

Metadata dan pembungkus

class ArtikelResource extends JsonResource
{
    public function with(Request $request): array
    {
        return ['versi_api' => 'v1'];
    }
}

// Menghilangkan pembungkus "data" โ€” putuskan SEKALI, di awal proyek
JsonResource::withoutWrapping();

// Status dan header saat dibuat
return ArtikelResource::make($artikel)
    ->response()
    ->setStatusCode(201)
    ->header('Location', route('api.artikel.show', $artikel));

Keputusan pembungkus data tidak bisa dicabut belakangan. Menghapusnya setelah ada klien yang berjalan adalah perubahan yang memutus semuanya sekaligus. Pilih di hari pertama dan konsisten. Kalau ragu: pertahankan data โ€” ia menyisakan ruang untuk menambahkan meta dan links nanti tanpa bertabrakan dengan field milikmu.

JSON:API bawaan Laravel 13

use Illuminate\Http\Resources\Json\JsonApiResource;

class ArtikelResource extends JsonApiResource
{
    public function toAttributes(Request $request): array
    {
        return [
            'judul' => $this->judul,
            'slug'  => $this->slug,
        ];
    }

    public function toRelationships(Request $request): array
    {
        return [
            'penulis' => fn () => PenulisResource::make($this->penulis),
        ];
    }
}

Bentuk JSON:API menangani serialisasi objek, penyertaan relasi (?include=penulis), sparse fieldset (?fields[artikel]=judul), dan header yang sesuai spesifikasi. Layak dipakai kalau konsumen API-mu banyak dan perlu mengambil bentuk yang berbeda-beda; berlebihan kalau konsumennya cuma frontend milikmu sendiri.

Versi API

// routes/api.php
Route::prefix('v1')->name('api.v1.')->group(base_path('routes/api_v1.php'));
Route::prefix('v2')->name('api.v2.')->group(base_path('routes/api_v2.php'));
app/Http/Resources/V1/ArtikelResource.php
app/Http/Resources/V2/ArtikelResource.php

Aturan yang menentukan kapan versi baru diperlukan. Menambah field itu aman โ€” klien lama mengabaikannya. Yang memutus: menghapus field, mengganti namanya, mengubah tipenya, atau mengubah arti sebuah nilai. Kalau salah satu terjadi, itu versi baru. Resource membuat perbedaan ini terlihat, karena semua field yang dikirim tertulis di satu berkas yang bisa dibaca dalam sepuluh detik.

Latihan: buat ArtikelResource yang menyertakan penulis lewat whenLoaded, panggil endpoint-nya tanpa with('penulis'), dan perhatikan field penulis hilang. Tambahkan with()-nya, hitung query dengan DB::listen(), dan pastikan jumlahnya tetap dua berapa pun jumlah artikelnya.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.