Pendahuluan — kenapa batasi request setelah Feature Test
Artikel ini adalah #69 (ini) di Seri 5: Laravel Lanjutan. Setelah bentuk jawaban JSON pinjam dikunci lewat Feature Test API (#68), pertanyaan berikutnya muncul: bagaimana kalau ada yang memanggil API berulang-ulang sampai server kewalahan?
Tanpa batas, satu skrip bisa menekan tombol pinjam ratusan kali per menit — antrean di loket perpustakaan jadi kacau. Hari ini kita belajar rate limiting: petugas loket yang menahan antrean — maksimal N request per menit; kalau lebih, respons 429 dengan pesan “coba lagi nanti”.
Awam: bayangkan petugas loket perpustakaan yang berkata: “Maksimal lima orang per menit.” Yang datang ke-enam harus menunggu. Itu peran throttle dan RateLimiter setelah Feature Test memastikan JSON tetap benar.
Prasyarat: sudah selesai Feature Test API (#68), paham fondasi Instal PHP, Composer & Proyek Laravel (#56) / Struktur Folder,
.env& Artisan Laravel (#57). Pakai Laravel 13+ — butuh PHP 8.3+.
Spesifikasi fitur — apa yang selesai hari ini?
Tiga hal ini yang kita kejar:
- Hitung request dalam jendela waktu — misalnya maksimal 5 panggilan per menit per IP.
- Tolak yang melewati batas — respons HTTP
429 Too Many Requestsdengan pesan “coba lagi nanti”. - Loloskan yang masih di bawah batas — respons normal
200seperti biasa.
Awam: selesai artikel ini, kamu punya pola batas yang melindungi API pinjam dari spam — bukan mengganti validasi atau otorisasi, melainkan menahan banjir panggilan berulang. Fokus kita RateLimiter bawaan Laravel dengan middleware throttle. PHP murni dulu supaya logika hitung-cek-tolak terlihat jelas.
Istilah — ringkas untuk rate limiting
| Istilah | Arti awam | Catatan |
|---|---|---|
limit |
Batas maksimal — misalnya 5 kali per menit | Angka yang tidak boleh dilampaui |
throttle |
Middleware yang memperlambat/memblokir request berlebihan | Seperti petugas loket yang menahan antrean |
RateLimiter |
Kelas Laravel untuk mendefinisikan aturan batas | Didaftarkan di AppServiceProvider |
429 |
Status HTTP “terlalu banyak request” | Artinya: coba lagi nanti, jangan spam |
| Jendela waktu | Rentang hitungan — misalnya 60 detik terakhir | Request lama di luar jendela tidak dihitung |
| Per IP | Batas dihitung per alamat pengirim | Satu pengguna spam tidak menghabiskan kuota semua orang |
Urutan belajar kita: fungsi hitung PHP dulu -> demo pass/fail -> baru cuplikan RateLimiter Laravel. Kalau loncat langsung ke middleware tanpa paham apa yang dihitung, batas sering ditulis asal-asalan.
Persiapan — alat yang kamu buka
Alat yang dipakai di artikel ini (fondasi dari Instal PHP, Composer & Proyek Laravel (#56) dan Struktur Folder, .env & Artisan Laravel (#57) — tidak ada unduhan Composer baru hari ini):
- Explorer — cek folder proyek
perpustakaan-api, lalu lihatroutes\api.phpataubootstrap\app.phpuntuk rute pinjam, sertaapp\Providers\AppServiceProvider.phpuntuk mendaftarkanRateLimiter. - Terminal — Laragon: menu Terminal · XAMPP: tombol Shell. Hindari CMD/PowerShell dari Start Menu kalau PATH PHP-mu belum rapi.
- Editor teks — Notepad / VS Code — untuk membuka
AppServiceProvideratau file rute. Contoh:notepad app\Providers\AppServiceProvider.php. - Browser — opsional. Inti uji hari ini ada di terminal; browser berguna kalau kamu sudah menjalankan
php artisan servedan ingin bandingkan dengancurl.exe.
Awam: untuk artikel ini satu terminal sebenarnya cukup — jalankan php laravel_rate_limiting_api_demo.php di folder proyek. Kalau php artisan serve dari artikel sebelumnya masih hidup, pakai terminal kedua untuk demo PHP dan perintah curl.exe berulang saat ingin melihat respons 429 di rute pinjam. Kalau butuh jendela kedua: Laragon — klik menu Terminal lagi · XAMPP — klik tombol Shell lagi, lalu cd ke folder proyek yang sama.
Buka terminal Laragon/Shell XAMPP, masuk ke folder proyek:
cd C:\laragon\www\perpustakaan-api
Di XAMPP biasanya: cd C:\xampp\htdocs\perpustakaan-api. Sesuaikan kalau foldermu beda.
Install-dari-nol: kalau php atau composer belum dikenali terminal, kembali dulu ke Instal PHP, Composer & Proyek Laravel (#56). Kalau struktur folder proyek masih membingungkan, ulangi Struktur Folder, .env & Artisan Laravel (#57).
Kenapa PHP biasa dulu?
Kalau langsung loncat ke middleware throttle di Laravel, pemula sering bingung: apa yang sebenarnya dihitung? Maka kita mulai dari fungsi PHP biasa yang menghitung request dalam jendela waktu — supaya perbedaan lolos vs ditolak terlihat jelas sebelum dibungkus RateLimiter.
<?php
// Mini: hitung request dalam jendela 60 detik, batas 3.
$limit = 3;
$window = 60;
$now = time();
$diBawahBatas = [$now - 10, $now - 20];
$diAtasBatas = [$now - 1, $now - 2, $now - 3, $now - 4];
function bolehLewat(array $waktuHit, int $limit, int $window, int $now): bool
{
$masihAktif = array_filter($waktuHit, fn (int $t) => ($now - $t) < $window);
return count($masihAktif) < $limit;
}
echo bolehLewat($diBawahBatas, $limit, $window, $now) ? "LOLOS" : "GAGAL", PHP_EOL;
echo bolehLewat($diAtasBatas, $limit, $window, $now) ? "LOLOS" : "GAGAL", PHP_EOL;
Awam — cara menguji bagian ini: salin potongan di atas ke file misalnya batas-cek.php, lalu di terminal Laragon/XAMPP jalankan php batas-cek.php. Kalau muncul LOLOS lalu GAGAL, ide “hitung dalam jendela waktu” sudah terlihat — yang di bawah batas lolos, yang melewati batas ditolak.
Alur batas — hitung, cek, tolak
Gerakan yang benar selalu sama:
- Hitung — berapa request masuk dalam jendela waktu (misalnya 60 detik terakhir).
- Cek — bandingkan dengan
limityang kamu tetapkan. - Tolak — kalau sudah penuh, kembalikan
429dengan pesan “coba lagi nanti”. - Loloskan — kalau masih di bawah batas, lanjutkan ke logika pinjam biasa.
<?php
// Salin ke file misalnya batas-cek.php lalu jalankan: php batas-cek.php
$limit = 5;
$window = 60;
$now = time();
$riwayat = [$now - 5, $now - 10, $now - 15, $now - 20, $now - 25, $now - 30];
function hitungAktif(array $waktuHit, int $window, int $now): int
{
return count(array_filter($waktuHit, fn (int $t) => ($now - $t) < $window));
}
function cekBatas(array $waktuHit, int $limit, int $window, int $now): array
{
$aktif = hitungAktif($waktuHit, $window, $now);
if ($aktif >= $limit) {
return ["lolos" => false, "status" => 429, "pesan" => "coba lagi nanti"];
}
return ["lolos" => true, "status" => 200, "pesan" => "ok"];
}
$hasil = cekBatas($riwayat, $limit, $window, $now);
echo $hasil["lolos"] ? "CEK LOLOS" : "CEK GAGAL 429", PHP_EOL;
echo $hasil["status"], " — ", $hasil["pesan"], PHP_EOL;
Awam — cara menguji bagian ini: salin potongan di atas ke batas-cek.php, lalu di terminal jalankan php batas-cek.php. Kalau muncul CEK GAGAL 429 dan 429 — coba lagi nanti, fondasi tolak sudah sehat. Ini versi PHP murni dari apa yang nanti ditulis sebagai RateLimiter::for di Laravel.
Laravel — cuplikan Rate Limiter (bukan file mandiri)
Di proyek Laravel, aturan batas didaftarkan di app\Providers\AppServiceProvider.php, lalu dipasang ke rute pinjam lewat middleware throttle.
<?php
// Cuplikan Laravel (bukan file mandiri)
// app/Providers/AppServiceProvider.php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
public function boot(): void
{
RateLimiter::for("pinjam", function (Request $request) {
return Limit::perMinute(5)->by($request->ip());
});
}
Pasang middleware ke rute pinjam di routes\api.php:
<?php
// Cuplikan Laravel (bukan file mandiri)
// routes/api.php
Route::middleware("throttle:pinjam")->post("/api/pinjam", [PeminjamanController::class, "store"]);
Awam: RateLimiter::for('pinjam', ...) = aturan “maksimal 5 per menit per IP”. throttle:pinjam = middleware yang menerapkan aturan itu sebelum logika pinjam dijalankan. Cuplikan ini bukan file mandiri — tempel ke proyek kalau rute pinjam sudah ada.
Uji dengan curl.exe berulang di terminal (kalau php artisan serve sudah jalan):
curl.exe -X POST "http://127.0.0.1:8000/api/pinjam" -H "Content-Type: application/json" -d "{\"buku_id\":1}"
Jalankan perintah di atas lebih dari 5 kali dalam satu menit — yang ke-enam harusnya mengembalikan 429.
Awam: curl.exe membantu melihat respons dengan mata — spam beberapa kali sampai muncul 429. Kalau muncul 404, rute pinjam mungkin belum dipasang — itu wajar; fokus dulu ke demo PHP di atas. Kalau php artisan serve belum jalan, cukup uji demo PHP; terminal kedua hanya untuk uji curl.exe berulang.
Pola Dasar — antrean yang terkendali
-
1
Tetapkan limit
Misalnya 5 request per menit per IP untuk rute pinjam. -
2
Daftarkan RateLimiter
RateLimiter::for('pinjam', ...)diAppServiceProvider. -
3
Pasang middleware throttle
throttle:pinjampada rute POST pinjam diroutes\api.php. -
4
Hitung dalam jendela
Laravel menghitung otomatis — mirip fungsihitungAktifdi demo PHP. -
5
Tolak dengan 429
Request ke-enam dalam menit yang sama mendapat429“coba lagi nanti”. -
6
Uji berulang
curl.exespam di terminal kedua — pastikan429muncul setelah batas tercapai.
Kode lengkap — demo mandiri
Simpan sebagai laravel_rate_limiting_api_demo.php, lalu jalankan php laravel_rate_limiting_api_demo.php:
<?php
declare(strict_types=1);
$limit = 3;
$window = 60;
$now = time();
$riwayatDiBawah = [$now - 10, $now - 20];
$riwayatDiAtas = [$now - 1, $now - 2, $now - 3, $now - 4];
function hitungAktif(array $waktuHit, int $window, int $now): int
{
return count(array_filter($waktuHit, fn (int $t) => ($now - $t) < $window));
}
function cekRateLimit(array $waktuHit, int $limit, int $window, int $now): array
{
$aktif = hitungAktif($waktuHit, $window, $now);
if ($aktif >= $limit) {
return ["lolos" => false, "status" => 429, "pesan" => "coba lagi nanti"];
}
return ["lolos" => true, "status" => 200, "pesan" => "ok"];
}
function demo(string $judul, array $waktuHit, int $limit, int $window, int $now): void
{
echo "=== {$judul} ===", PHP_EOL;
$hasil = cekRateLimit($waktuHit, $limit, $window, $now);
echo $hasil["lolos"] ? "LOLOS" : "GAGAL", PHP_EOL;
echo "HTTP ", $hasil["status"], " — ", $hasil["pesan"], PHP_EOL, PHP_EOL;
}
demo("Di bawah batas — harus lolos", $riwayatDiBawah, $limit, $window, $now);
demo("Di atas batas — harus gagal 429", $riwayatDiAtas, $limit, $window, $now);
demo("Tepat di batas — harus gagal 429", [$now - 1, $now - 2, $now - 3], $limit, $window, $now);
Awam — cara menguji bagian ini: simpan file sebagai laravel_rate_limiting_api_demo.php di folder proyek, lalu di terminal Laragon/XAMPP jalankan php laravel_rate_limiting_api_demo.php. Harusnya muncul satu LOLOS lalu dua GAGAL dengan HTTP 429 — coba lagi nanti. Fungsi cekRateLimit adalah inti logika; demo(...) hanya membungkus output agar mudah dibaca di terminal — mirip apa yang dilakukan RateLimiter di Laravel.
Kesalahan umum
| Gejala | Penyebab tipikal | Perbaikan awam |
|---|---|---|
| Semua request ditolak, termasuk yang pertama | limit diset 0 atau jendela terlalu sempit |
Cek angka di Limit::perMinute(5) |
| Spam tidak pernah kena 429 | Middleware throttle belum dipasang ke rute |
Tambah throttle:pinjam di routes\api.php |
| Satu pengguna spam menghabiskan kuota semua orang | Batas global, bukan per IP | Pakai ->by($request->ip()) di RateLimiter |
curl.exe selalu 404 |
Rute pinjam belum dipasang atau serve belum jalan |
Fokus demo PHP dulu; 404 wajar kalau rute belum ada |
| 429 tidak muncul meski spam | Cache driver tidak jalan di lokal | Pastikan CACHE_STORE di .env bukan array untuk uji throttle |
| Batas terlalu ketat untuk penggunaan normal | limit terlalu rendah untuk rute baca |
Pisahkan limit: ketat untuk POST pinjam, longgar untuk GET daftar |
Latihan singkat
- Ubah demo: set
$limit = 5dan buat skenario yang lolos vs gagal. - Jelaskan ke teman: beda petugas loket yang menahan antrean vs tidak ada batas sama sekali — pakai analogi perpustakaan.
- Tulis satu kalimat: kenapa
429lebih baik daripada membiarkan server crash karena spam.
FAQ singkat
Apakah rate limiting menggantikan Feature Test?
Tidak. Feature Test dari Feature Test API (#68) mengunci bentuk JSON. Rate limiting melindungi API dari spam request berulang — tugas berbeda, keduanya saling melengkapi.
Haruskah batas sama untuk semua rute?
Tidak wajib. Rute POST pinjam biasanya lebih ketat (misalnya 5/menit) daripada GET daftar buku (misalnya 60/menit). Sesuaikan dengan beban nyata perpustakaan mini.
Tool apa yang dibuka dulu?
Explorer untuk memastikan folder proyek benar (routes\api.php + AppServiceProvider), satu terminal untuk demo PHP, editor untuk cuplikan Laravel. Kalau serve hidup, terminal kedua untuk curl.exe spam.
Potongan sintaks diuji di mana?
Langkah tengah (fungsi hitung jendela) salin ke batas-cek.php, lalu jalankan php batas-cek.php. Demo lengkap diuji dengan php laravel_rate_limiting_api_demo.php. Cuplikan Laravel ditempel ke app\Providers\AppServiceProvider.php dan routes\api.php; uji throttle dengan curl.exe berulang.
Ke mana setelah ini?
Berikutnya alami: Capstone: Pinjam & Kembalikan (#70) — satukan semua potongan ke alur pinjam–kembali utuh.
Kesimpulan
Kamu sudah melindungi API pinjam dari spam dengan rate limiting: fungsi hitung PHP dulu di batas-cek.php, demo pass/fail di laravel_rate_limiting_api_demo.php, lalu cuplikan RateLimiter dan middleware throttle Laravel. Petugas loket digital — maksimal N request per menit, sisanya 429 “coba lagi nanti”.
Seri 5 progress: langkah #69 (ini) · 6/7 Laravel Lanjutan · prasyarat: Feature Test API (#68) LIVE. Berikutnya: Capstone: Pinjam & Kembalikan (#70).