API Resource adalah class yang mengubah model Eloquent jadi JSON dengan bentuk yang kamu tentukan sendiri. Bayangin arsip anggota di meja petugas perpustakaan. Lembar arsipnya lengkap: nomor HP, catatan denda, status petugas. Kartu anggota yang kamu pegang cuma memuat nama, nomor anggota, dan tanggal bergabung, dengan tata letak yang sama untuk semua anggota. Model itu lembar arsipnya, baris lengkap dari database. Resource itu pencetak kartunya: dia memilih field mana yang keluar dan merapikan bentuknya.
🧪 Tebakan cepat: controller menulis
return new UserResource($user);. JSON yang diterima client diawali{"id": 1, ...}atau{"data": {"id": 1, ...}}? Tebak dulu, jawabannya ada di bagian "Satu, banyak, dan per halaman".
Kenapa gak return model langsung
Laravel bisa mengubah model jadi JSON tanpa bantuan. Route ini mengembalikan user Budi apa adanya:
Route::get('/api/users/{id}', fn ($id) => User::find($id));
{"id":1,"name":"Budi","email":"[email protected]","phone":"081234567890","is_admin":0,"created_at":"2026-03-14T09:30:00.000000Z","updated_at":"2026-03-14T09:30:00.000000Z"}
password dan remember_token gak ikut, karena model User bawaan Laravel sudah menandai keduanya sebagai tersembunyi. Tapi kolom lain ikut semua: nomor HP anggota dan is_admin bocor ke siapa pun yang membuka alamat ini, tanggalnya berbentuk mentah dengan mikrodetik, dan bentuk JSON-nya ikut berubah tiap kali tabelnya berubah. Resource membalik caranya: cuma field yang kamu tulis yang keluar.
Membuat resource
php artisan make:resource UserResource
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'member_since' => $this->created_at->format('Y-m-d'),
];
}
}
php artisan make:resource UserResourcemembuat fileapp/Http/Resources/UserResource.php.extends JsonResourcemembuat class ini resource. Semua kerjanya ada di methodtoArray($request).$this->id,$this->name,$this->emailmembaca kolom dari model yang dibungkus. Di dalam resource,$thismeneruskan ke model itu.$this->created_at->format('Y-m-d'):created_atberupa objek tanggal (Carbon), danformat('Y-m-d')mengubahnya jadi teks2026-03-14.phone,is_admin,passworddanupdated_atgak ditulis, jadi gak pernah keluar, termasuk kolom baru yang ditambahkan nanti.
Satu, banyak, dan per halaman
Resource dipakai di controller dengan tiga cara. Datanya tiga anggota perpustakaan: Budi, Ani, Citra.
| Di controller | JSON yang diterima client |
|---|---|
return new UserResource(User::find(1)); |
{"data":{"id":1,"name":"Budi","email":"[email protected]","member_since":"2026-03-14"}} |
return UserResource::collection(User::all()); |
{"data":[{...Budi},{...Ani},{...Citra}]} |
return UserResource::collection(User::paginate(2)); |
data berisi Budi dan Ani, ditambah links dan meta |
Resource paling luar selalu dibungkus key data. Jadi JSON-nya diawali {"data": {...}}. Itu jawaban tebakannya. Untuk banyak model, pakai UserResource::collection(...), bukan new UserResource(...). Hasil paginate(2) membawa keterangan halaman:
"links": {
"first": "http://127.0.0.1:8000/api/users?page=1",
"last": "http://127.0.0.1:8000/api/users?page=2",
"prev": null,
"next": "http://127.0.0.1:8000/api/users?page=2"
},
"meta": { "current_page": 1, "from": 1, "last_page": 2, "path": "http://127.0.0.1:8000/api/users", "per_page": 2, "to": 2, "total": 3, "links": [...] }
Di halaman 2, next jadi null dan prev berisi alamat halaman 1. Cara kerja halaman sendiri dibahas di pelajaran Pagination.
Field bersyarat: when dan whenLoaded
return [
'id' => $this->id,
'name' => $this->name,
'phone' => $this->when($request->user()?->is_admin, $this->phone),
'loans' => LoanResource::collection($this->whenLoaded('loans')),
];
$this->when(syarat, nilai): kalau syaratnyatrue, keyphonekeluar. Kalaufalse, key-nya hilang sama sekali, bukan jadinull. Di sini nomor HP cuma terlihat oleh admin.$this->whenLoaded('loans'): keyloanscuma keluar kalau relasinya sudah dimuat di controller, misalnyaUser::with('loans')->find(1). Tanpawith, key-nya hilang dan gak ada query tambahan.LoanResource::collection(...)membungkus tiap peminjaman dengan resource-nya sendiri, jadi relasi juga punya bentuk yang terkontrol.
Di latihan
Kode awal sudah punya class UserResource extends JsonResource dengan toArray($request) yang kosong. Isi array-nya dengan id, name, email dan member_since. Simulator latihan memuat class-nya lalu mencetak Class defined: UserResource. Isinya dinilai dari kodenya: harus ada $this->..., ->format( dan Y-m-d.
⚠️ Jebakan yang sering ketemu
new UserResource(User::all()). Collection bukan satu model, jadi$this->idgak ada:Property [id] does not exist on this collection instance.Banyak model pakaiUserResource::collection(...).- Tanggal dikirim mentah.
'member_since' => $this->created_atkeluar sebagai2026-03-14T09:30:00.000000Z. Format dulu dengan->format('Y-m-d'). $this->loanslangsung, tanpawhenLoaded. Untuk 3 anggota, Laravel mengirim 1 query users plus 3 query loans, satu per anggota (N+1, dibahas di Relasi Eloquent).- Menulis
$user->namedi dalam resource. DitoArraygak ada variabel$user, jadi PHP berhenti denganUndefined variable $user. Model-nya dibaca lewat$this->name.
TL;DR: API Resource memilih dan merapikan field yang keluar dari model. Buat dengan php artisan make:resource UserResource, turunkan dari JsonResource, lalu kembalikan array di toArray($request): 'id' => $this->id, 'member_since' => $this->created_at->format('Y-m-d'). Kolom yang gak ditulis gak pernah keluar. Satu model: return new UserResource($user). Banyak model: return UserResource::collection(User::all()). Hasilnya dibungkus {"data": ...}, dan paginate() menambahkan links dan meta. $this->when(syarat, nilai) menghilangkan key saat syaratnya false, $this->whenLoaded('loans') cuma menampilkan relasi yang sudah dimuat dengan with('loans').