Laravel · Bab 13
Lab 9: API Tulis, Resource & Validasi
Melengkapi API dengan endpoint tulis — membentuk JSON lewat JsonResource, memvalidasi dengan FormRequest (422), serta memakai kode status 201 dan 204 dengan benar.
Lab 8 baru bisa membaca. Di lab ini API belajar menulis: menambah, mengubah, dan menghapus data. Tantangannya ada di dua hal yang tidak ada di halaman web biasa: bentuk JSON yang rapi dan konsisten, serta pesan kesalahan yang bisa dibaca mesin. Contoh kode memakai studi kasus perpustakaan (books); terapkan polanya ke produk.
Alur Langkah
- Buat
ProductResourceuntuk membentuk JSON satu produk. - Buat
ProductRequest(FormRequest) berisi aturan validasi. - Lengkapi controller:
index()/show()memakai Resource, lalustore(),update(),destroy(). - Buka rute tulis di
routes/api.phpdan uji semua kode status di tab Klien API.
JsonResource: Kontrak Bentuk JSON
Mengembalikan model Eloquent langsung ke JSON membocorkan semua kolom, termasuk yang tidak ingin kamu tampilkan, dan bentuknya ikut berubah setiap kali tabel berubah. JsonResource adalah lapisan penerjemah yang menetapkan bentuk JSON secara eksplisit:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class BookResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'author' => $this->whenLoaded('author', fn () => [
'id' => $this->author->id,
'name' => $this->author->name,
]),
];
}
}
Di controller, satu data dibungkus new BookResource($book) dan koleksi memakai BookResource::collection(...). Laravel otomatis membungkus hasilnya dalam kunci data. whenLoaded() hanya menyertakan relasi bila controller memuatnya dengan with() atau load(), sehingga tidak terjadi kueri tambahan yang tak disengaja (masalah N+1).
FormRequest: Validasi yang Rapi
Di Lab 4 validasi ditulis di dalam controller. Pada API, aturan validasi lebih rapi dipindahkan ke kelas tersendiri:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class BookRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'isbn' => ['required', 'max:13', Rule::unique('books', 'isbn')->ignore($this->route('book'))],
'title' => 'required|max:255',
'year' => 'required|integer|min:0',
];
}
}
authorize() menentukan apakah pengguna boleh melakukan request ini; bawaan artisan false sehingga semua request ditolak 403 sampai kamu mengubahnya. rules() berisi aturan yang sama seperti $request->validate([...]). Rule::unique(...)->ignore(...) adalah bentuk lain dari unique:tabel,kolom,id di Lab 6: $this->route('book') mengambil buku yang sedang diubah, atau null saat membuat buku baru.
Di controller, cukup ketik-hint kelas ini, dan validasi berjalan sebelum method dieksekusi:
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Http\Requests\BookRequest;
use App\Http\Resources\BookResource;
use App\Models\Book;
class BookController extends Controller
{
public function store(BookRequest $request)
{
$book = Book::create($request->validated());
return (new BookResource($book->load('author')))
->response()
->setStatusCode(201);
}
}
$request->validated() hanya berisi data yang lolos aturan, sehingga aman dipakai dengan $fillable. Pada API, validasi yang gagal tidak mengarahkan balik ke form seperti di web. Selama kamu mengirim Accept: application/json, Laravel menjawab 422 dengan JSON:
{
"message": "The title field is required.",
"errors": { "title": ["The title field is required."] }
}
Klien (nanti aplikasi Vue) membaca errors untuk menampilkan pesan di bawah kolom yang tepat.
Kode Status yang Benar
| Aksi | Berhasil | Gagal |
|---|---|---|
| GET daftar / satu data | 200 | 404 bila tidak ada |
| POST buat data | 201 Created | 422 bila data tidak valid |
| PUT ubah data | 200 | 404 / 422 |
| DELETE hapus data | 204 No Content | 404 |
Kode status adalah bagian dari kontrak API: klien memutuskan apa yang dilakukan dari angka itu, bukan dari teks pesan. Untuk 204 tidak ada isi respons sama sekali, sehingga controller memakai return response()->noContent();. Untuk 201, JsonResource sebenarnya sudah menjawab 201 bagi model yang baru dibuat (wasRecentlyCreated), jadi setStatusCode(201) membuatnya eksplisit, bukan keharusan.
Saat mengirim body JSON dari Klien API atau axios, sertakan juga Content-Type: application/json supaya Laravel membaca body sebagai JSON (Klien API menambahkannya otomatis).
Hasil yang Diharapkan
POST data valid menjawab 201 dan data muncul di daftar. POST data tidak lengkap menjawab 422 berisi pesan per kolom. PUT mengubah data tanpa bentrok dengan kodenya sendiri. DELETE menjawab 204, dan mengambil data yang sama sesudahnya menjawab 404.