Pendahuluan — satpam di pintu toko
Artikel ini adalah #59 (ini) di Seri 4: Pemrograman Web Lanjut v2. Di Routing & Jawaban JSON API Perpustakaan (#58) kamu sudah buka pintu HTTP dan jawab JSON. Sekarang langkah 4/8: jaga isi permintaan yang masuk lewat pintu supaya data tidak berantakan.
Awam: pintu sudah ada. Satpam (validasi) memeriksa slip formulir: judul buku ada? penulis diisi? Kalau slip kosong/kotor, satpam menolak dengan jawaban rapi — bukan menerima data asal-asalan.
Domain tetap perpustakaan mini. Hari ini kita menerima permintaan tambah buku (POST) dengan aturan sederhana. Menyimpan ke tabel database dan login datang belakangan.
Prasyarat: sudah selesai Routing & Jawaban JSON API Perpustakaan (#58) — route
GET /api/bukupernah jalan. Fondasi di Struktur Folder,.env& Artisan Laravel (#57) dan Instal PHP, Composer & Proyek Laravel (#56). Pakai Laravel 13+ — butuh PHP 8.3+.
Spesifikasi fitur — apa yang kita kuasai?
Daftar singkat yang bisa kamu centang di akhir artikel:
- Mengerti Request sebagai isi permintaan yang masuk lewat pintu HTTP.
- Memakai
$request->validate([...])untuk cek judul & penulis. - Mengenal Form Request sebagai file aturan khusus (lebih rapi).
- Membaca jawaban gagal 422 sebagai “data belum lolos cek”.
Awam: urutan nyaman: paham Request -> cek di route -> pindah aturan ke Form Request -> uji gagal & sukses. Belum perlu menyimpan ke tabel database di sini.
Istilah — ringkas untuk satpam & slip
| Istilah | Arti awam | Contoh di artikel ini |
|---|---|---|
| Request | Isi permintaan yang datang lewat pintu | Judul & penulis buku baru |
| Validasi | Cek slip: wajib diisi? bentuknya benar? | judul & penulis required |
| Form Request | File aturan satpam khusus (bukan di route) | StoreBukuRequest |
| Status 422 | Ditolak karena data belum lolos cek | Judul kosong -> JSON error |
| HTTP POST | Permintaan “tolong terima data baru” | POST /api/buku |
Urutan belajar: baca istilah -> lihat alur -> cek di PHP -> Form Request -> uji.
Kenapa jaga input dulu?
Tanpa validasi, API menerima judul kosong atau data aneh. Nanti saat menyimpan ke database, masalahnya makin sulit dilacak.
Awam: satpam memeriksa slip sebelum buku masuk rak. Lebih baik ditolak di pintu daripada rak penuh kertas kosong.
Artikel ini tetap install-dari-nol untuk validasi: tidak ada paket baru. Kita pakai Request & Artisan bawaan Laravel. Fondasi PHP/Composer/Laravel + denah + routing sudah di Instal PHP, Composer & Proyek Laravel (#56), Struktur Folder, .env & Artisan Laravel (#57), dan Routing & Jawaban JSON API Perpustakaan (#58).
Alur — dari POST sampai lolos/ditolak
Persiapan — toko & pintu masih hidup
Alat yang dipakai di artikel ini (semua sudah dari fondasi Instal PHP, Composer & Proyek Laravel (#56) / Struktur Folder, .env & Artisan Laravel (#57) — tidak ada unduhan wajib baru):
- Explorer — pastikan folder
perpustakaan-apiada; cekroutes/web.phpdan nantibootstrap/app.php. - Terminal — Laragon: menu Terminal · XAMPP: tombol Shell. Jangan asal CMD/PowerShell dari Start Menu (PATH PHP bisa hilang).
- Terminal kedua — wajib: terminal pertama =
php artisan serve. Terminal kedua = ujicurl.exe/ PowerShell / edit file. - Editor teks — Notepad / VS Code. Tip dari terminal kedua:
notepad routes\web.phpataunotepad bootstrap\app.php(harus sudahcdke folder proyek). - Browser — cukup untuk memastikan toko hidup (seperti di Routing & Jawaban JSON API Perpustakaan (#58)). Menguji POST tidak cukup ketik URL di bilah alamat; butuh perintah di terminal (atau alat uji API berjendela).
Buka terminal Laragon/Shell XAMPP (terminal pertama), masuk folder proyek:
cd C:\laragon\www\perpustakaan-api
Di XAMPP biasanya: cd C:\xampp\htdocs\perpustakaan-api. Sesuaikan path jika foldermu beda. Lalu:
php artisan serve
Biarkan jendela terminal ini tetap hidup (lampu toko). Cara buka terminal kedua: Laragon — klik menu Terminal sekali lagi; XAMPP — klik tombol Shell sekali lagi. Jangan matikan yang sedang serve.
Pastikan GET /api/buku dari Routing & Jawaban JSON API Perpustakaan (#58) masih ada (boleh dicek sekali di browser: http://127.0.0.1:8000/api/buku). Hari ini kita menambah pintu POST.
Awam: GET = “tolong kirim daftar”. POST = “tolong terima data baru”. Matikan toko sementara dengan Ctrl+C di terminal yang menjalankan serve.
PHP dulu — cek slip tanpa Laravel
Sebelum cuplikan Laravel, rasakan ide satpam di PHP biasa. Cuplikan di bawah boleh dibaca dulu — belum wajib disimpan. Kalau mau menjalankan di terminal, pakai file demo di bagian Demo satpam nanti (lebih lengkap).
<?php
declare(strict_types=1);
$input = [
'judul' => '',
'penulis' => 'Ayu',
];
$errors = [];
if (trim((string) ($input['judul'] ?? '')) === '') {
$errors['judul'] = 'Judul wajib diisi';
}
if (trim((string) ($input['penulis'] ?? '')) === '') {
$errors['penulis'] = 'Penulis wajib diisi';
}
if ($errors !== []) {
echo "Status awam: 422 (data belum lolos cek)", PHP_EOL;
echo json_encode(['message' => 'Validasi gagal', 'errors' => $errors], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), PHP_EOL;
} else {
echo "Status awam: OK — slip bersih", PHP_EOL;
}
Awam: array $errors = catatan satpam. Kalau ada isi, tolak dulu. Ide yang sama dipakai Laravel lewat validate / Form Request.
Validasi cepat di route
Buka editor teks, buka file routes/web.php di folder proyek. Tambahkan route POST (cuplikan — tempel di bawah route GET kamu), lalu simpan file:
// Cuplikan routes/web.php — POST tambah buku (validasi di route)
use Illuminate\Http\Request;
Route::post('/api/buku', function (Request $request) {
$data = $request->validate([
'judul' => ['required', 'string', 'max:120'],
'penulis' => ['required', 'string', 'max:80'],
]);
return response()->json([
'message' => 'Slip bersih — buku siap diproses (belum disimpan ke tabel)',
'data' => $data,
], 201);
});
Awam: required = wajib diisi. max:120 = maksimal 120 huruf/angka (supaya judul tidak kepanjangan). Kalau gagal, Laravel otomatis menjawab JSON error (sering status 422). Angka 201 = “data baru diterima” (belum berarti sudah di rak database).
Form Request — aturan di file sendiri
Kalau aturan makin panjang, pindahkan ke file khusus supaya route tidak penuh. Di terminal kedua (folder perpustakaan-api masih sama; serve tetap hidup di terminal pertama):
php artisan make:request StoreBukuRequest
Buka file yang dibuat di editor (biasanya app/Http/Requests/StoreBukuRequest.php). Di dalam kelas itu sudah ada kerangka fungsi — ganti isi authorize dan rules menjadi seperti cuplikan berikut, lalu simpan:
// Cuplikan StoreBukuRequest — aturan satpam
public function authorize(): bool
{
return true; // belum login — semua boleh coba kirim (untuk belajar)
}
public function rules(): array
{
return [
'judul' => ['required', 'string', 'max:120'],
'penulis' => ['required', 'string', 'max:80'],
];
}
Lalu di route, minta Laravel memakai satpam itu:
// Cuplikan routes/web.php — pakai Form Request
use App\Http\Requests\StoreBukuRequest;
Route::post('/api/buku', function (StoreBukuRequest $request) {
$data = $request->validated();
return response()->json([
'message' => 'Slip bersih lewat Form Request',
'data' => $data,
], 201);
});
Awam: validated() = ambil hanya field yang sudah lolos cek. authorize(): true di sini berarti “belum ada kartu anggota” — login dibahas belakangan. Artisan make:request sudah ada di proyek Laravel-mu (fondasi Instal PHP, Composer & Proyek Laravel (#56) / Struktur Folder, .env & Artisan Laravel (#57)) — tidak perlu unduh paket baru.
Cek izin api/* — supaya POST tidak ditolak 419
Sebelum menguji, ada satu satpam lobi lain yang perlu kita sapa. Laravel punya penjaga bawaan bernama CSRF yang melindungi formulir web: ia menolak semua kiriman POST (juga PUT dan DELETE) yang tidak datang dari halaman browser milik situs itu sendiri.
Masalahnya, kita menguji dari terminal pakai curl.exe — dan terminal bukan browser. Jadi tanpa izin khusus, uji POST di bawah akan dijawab:
{"message":"CSRF token mismatch."}
dengan kode 419. Ini bukan berarti slip atau satpam Form Request-mu salah — pintu kita bahkan belum sempat dijalankan.
Awam: bayangkan satpam lobi yang hanya mengizinkan orang masuk lewat pintu depan resmi. Kita datang dari pintu samping (terminal), jadi dia menahan kita. Kita perlu bilang: “alamat yang berawalan api/ itu memang dilayani dari luar lobi, tolong jangan ditahan.”
Kalau di dalam withMiddleware sudah ada tulisan 'api/*' — jangan ubah apa-apa; lanjut ke bagian Uji di bawah. Izin ini cukup sekali per proyek.
Belum ada? Pastikan terminal kedua sudah di folder proyek (cd ke perpustakaan-api, sama seperti saat serve). Lalu buka: notepad bootstrap\app.php. Cari bagian ->withMiddleware(...).
Awam — PENTING: jangan menempel blok ->withMiddleware(...) baru di dalam fungsi yang sudah ada (itu membuat fungsi bersarang dan Laravel error). Kerja di dalam fungsi yang sudah ada: kalau isinya masih //, hapus baris // itu; kalau sudah ada baris lain, biarkan. Lalu tempel hanya baris di bawah ini ke dalam fungsi:
// Tempel di dalam withMiddleware yang sudah ada — jangan buat withMiddleware baru
$middleware->preventRequestForgery(except: [
'api/*',
]);
Setelah disimpan, isi fungsimu kira-kira seperti ini (boleh ada baris lain di atas/bawahnya):
->withMiddleware(function (Middleware $middleware): void {
$middleware->preventRequestForgery(except: [
'api/*',
]);
})
Simpan file. Awam: except artinya “kecuali”. Bacanya: “periksa CSRF untuk semua halaman, kecuali alamat berawalan api/.” Tanda * = “apa pun setelahnya”, jadi api/buku ikut dikecualikan.
Catatan nama: di Laravel 13 nama resminya preventRequestForgery. Nama lama validateCsrfTokens masih diterima dan artinya sama — kalau kamu melihatnya di tempat lain, itu bukan kesalahan.
Apakah ini membuat API-ku tidak aman? Tidak. CSRF adalah pagar khusus formulir browser. Pengecualian api/* resmi untuk API yang diuji dari luar browser. Siapa yang boleh masuk pintu API dibahas belakangan di seri ini — hari ini kita hanya memastikan satpam lobi tidak menahan uji dari terminal.
Setiap kali menyimpan bootstrap\app.php, matikan serve di terminal pertama dengan Ctrl+C, lalu nyalakan lagi php artisan serve. File itu hanya dibaca saat aplikasi mulai. Izin ini berlaku untuk semua artikel berikutnya di seri ini.
Uji gagal & sukses
Pastikan php artisan serve masih hidup di terminal pertama. Buka terminal kedua untuk menguji. Kita kirim data JSON — bilah alamat browser saja tidak cukup untuk POST.
Opsi A — curl di terminal (Windows 10/11 biasanya sudah punya; di PowerShell ketik curl.exe agar tidak tertukar dengan perintah lain):
# Sengaja kosongkan judul — harus ditolak (422)
curl.exe -s -X POST http://127.0.0.1:8000/api/buku ^
-H "Content-Type: application/json" ^
-H "Accept: application/json" ^
-d "{\"judul\":\"\",\"penulis\":\"Ayu\"}"
Awam — soal tanda ^ di ujung baris: tanda itu berarti “perintah ini masih lanjut ke baris berikutnya”, dan berlaku di CMD / Shell XAMPP / Terminal Laragon. Kalau kamu memakai PowerShell, ^ tidak dikenal — ganti setiap ^ dengan tanda backtick `, atau yang paling aman: ketik seluruh perintah dalam satu baris panjang tanpa ^. Di Mac/Linux, ganti ^ dengan \ atau satu baris.
# Slip bersih — harus 201 + JSON data
curl.exe -s -X POST http://127.0.0.1:8000/api/buku ^
-H "Content-Type: application/json" ^
-H "Accept: application/json" ^
-d "{\"judul\":\"Belajar Laravel\",\"penulis\":\"Budi\"}"
Opsi B — PowerShell (kalau kutip di curl ribet), tempel satu perintah per uji:
# Judul kosong — harap 422
Invoke-RestMethod -Method Post -Uri http://127.0.0.1:8000/api/buku `
-ContentType 'application/json' -Headers @{ Accept = 'application/json' } `
-Body '{"judul":"","penulis":"Ayu"}'
# Slip bersih — harap data + status sukses
Invoke-RestMethod -Method Post -Uri http://127.0.0.1:8000/api/buku `
-ContentType 'application/json' -Headers @{ Accept = 'application/json' } `
-Body '{"judul":"Belajar Laravel","penulis":"Budi"}'
Opsi C — alat uji API berjendela (misalnya Postman atau Insomnia — opsional, gratis). Isi: metode POST, URL http://127.0.0.1:8000/api/buku, header Accept: application/json + Content-Type: application/json, body JSON sama seperti di atas. Ide utamanya sama: kirim slip, baca OK atau 422.
Awam: baris Accept: application/json meminta jawaban berbentuk JSON (bukan halaman HTML error). Kalau muncul error HTML panjang, hampir selalu karena header Accept belum ada.
Pola Dasar — empat langkah satpam bersih
-
1
Nyalakan serve
php artisan serve+ pintu GET dari artikel routing JSON sebelumnya masih ada. -
2
Cek di route dulu
POST /api/buku+$request->validateuntuk judul & penulis. -
3
Pindah ke Form Request
php artisan make:request StoreBukuRequestlaluvalidated(). -
4
Uji 422 & 201
Di terminal kedua:curl.exe/ PowerShell / alat berjendela — judul kosong (ditolak) lalu slip bersih.
Demo satpam — file mandiri
Latihan ide satpam tanpa menyentuh Laravel:
- Buka editor teks, buat file baru, tempel cuplikan di bawah, simpan sebagai
laravel_request_validasi_api_demo.php(boleh di Desktop atau folder latihan — tidak harus di dalamperpustakaan-api). - Buka terminal di folder tempat file itu disimpan (Windows Explorer: Shift+klik kanan folder -> “Open in Terminal” / “Buka di Terminal”, atau
cdmanual). - Jalankan:
php laravel_request_validasi_api_demo.php— layar harus menampilkan kasus 422 lalu OK.
File ini hanya mensimulasikan cek slip — tidak mengubah proyek Laravel-mu:
<?php
declare(strict_types=1);
/**
* Demo satpam Request/validasi — simulasi teks untuk awam.
* File: laravel_request_validasi_api_demo.php
*/
function cekSlip(array $input): array
{
$errors = [];
if (trim((string) ($input['judul'] ?? '')) === '') {
$errors['judul'] = 'Judul wajib diisi';
}
if (trim((string) ($input['penulis'] ?? '')) === '') {
$errors['penulis'] = 'Penulis wajib diisi';
}
return $errors;
}
function demo(): void
{
$kasus = [
['judul' => '', 'penulis' => 'Ayu'],
['judul' => 'Dasar Laravel', 'penulis' => 'Budi'],
];
foreach ($kasus as $i => $input) {
$n = $i + 1;
$errors = cekSlip($input);
echo "=== Kasus {$n} ===", PHP_EOL;
if ($errors !== []) {
echo "Hasil: 422 — data belum lolos cek", PHP_EOL;
echo json_encode(['errors' => $errors], JSON_UNESCAPED_UNICODE), PHP_EOL;
} else {
echo "Hasil: OK — slip bersih", PHP_EOL;
echo json_encode(['data' => $input], JSON_UNESCAPED_UNICODE), PHP_EOL;
}
echo PHP_EOL;
}
}
demo();
Awam: demo() hanya menampilkan dua kasus di terminal. Setelah paham, kerjakan langkah yang sama di routes/web.php / Form Request. declare(strict_types=1); membuat tipe lebih ketat — boleh diikuti, tidak wajib dihafal.
Kesalahan umum
| Gejala | Penyebab tipikal | Perbaikan awam |
|---|---|---|
CSRF token mismatch / kode 419 |
Izin api/* di bootstrap\app.php belum dipasang, atau serve belum dinyalakan ulang setelah menyimpannya |
Pasang preventRequestForgery(except: ['api/*']) (jangan timpa baris lain di withMiddleware), lalu Ctrl+C dan php artisan serve lagi |
| HTML error page, bukan JSON | Lupa baris Accept: application/json |
Tambah Accept saat uji POST |
curl tidak dikenal / error aneh di PowerShell |
Perintah tertukar atau belum memakai curl.exe |
Pakai curl.exe, Opsi B PowerShell, atau alat berjendela (Opsi C) |
| Connection refused / gagal sambung | php artisan serve belum hidup / terminal salah folder |
Nyalakan serve di folder perpustakaan-api, uji lagi di terminal kedua |
| 405 Method Not Allowed | Masih memakai GET untuk kirim data | Pakai POST ke /api/buku |
| Class StoreBukuRequest not found | File belum dibuat / nama kelas atau folder salah | Jalankan ulang make:request, cek folder app/Http/Requests |
| Selalu 422 meski sudah isi | Nama field beda (title vs judul) |
Samakan kunci JSON dengan aturan rules() |
Latihan
- Jalankan demo PHP di atas — pastikan kasus judul kosong ditolak.
- Buat
StoreBukuRequest, hubungkan kePOST /api/buku, uji 422 lalu 201. - Jelaskan ke teman: beda singkat “pintu (route)” dan “satpam (validasi/Form Request)” dengan bahasa toko.
FAQ
Harus Form Request dari hari pertama?
Tidak. Boleh mulai dari $request->validate di route. Form Request berguna saat aturan makin panjang atau dipakai di banyak tempat.
Harus install Postman?
Tidak wajib. Terminal + curl.exe / PowerShell sudah cukup. Alat berjendela hanya opsi kalau kutip di terminal terasa ribet.
Kenapa browser saja tidak cukup?
Browser mudah untuk GET (ketik URL). POST butuh mengirim body JSON — itu kerja terminal atau alat uji API, bukan bilah alamat.
Kenapa belum disimpan ke database?
Supaya fokus satu hal: menjaga input. Menyimpan rapi (pengatur kode + tabel) datang setelah fondasi satpam nyaman.
Apa hubungan dengan routing?
Routing & Jawaban JSON API Perpustakaan (#58) memasang pintu. #59 (ini) memasang satpam di pintu itu.
Ke mana setelah ini?
Berikutnya: Controller, Service & Eloquent Laravel (#60) — merapikan loket, dapur, dan cara baca baris di tabel.
Kesimpulan
Kamu sudah menjaga input API di Laravel: memahami Request, memakai validasi, mengenal Form Request, dan membedakan jawaban OK vs 422. Ini langkah 4/8 jalur Laravel di Seri 4.
Seri 4 progress: langkah #59 (ini) · 4/8 jalur Laravel · prasyarat: Routing & Jawaban JSON API Perpustakaan (#58) LIVE. Berikutnya: Controller, Service & Eloquent Laravel (#60).