Laravel membuat urusan mengembalikan JSON dari controller jadi gampang sekali, dan di situlah banyak proyek API berhenti memikirkan desainnya. Setahun kemudian tim mobile harus menangani empat format error yang berbeda, aplikasi web rusak setiap kali ada kolom yang diganti namanya, dan tidak ada yang yakin endpoint mana saja yang sudah mengecek hak akses. Beberapa kesepakatan yang dipasang sejak awal bisa mencegah sebagian besar masalah ini. Contoh di bawah memakai Laravel 11 ke atas, tapi idenya tetap berlaku di versi yang lebih lama.
Siapkan Route API dan Beri Versi
Sejak Laravel 11, aplikasi baru tidak langsung punya route API. Jalankan php artisan install:api untuk membuat routes/api.php sekaligus memasang Laravel Sanctum. Taruh semua route di bawah prefix versi seperti /api/v1 sejak hari pertama. Aplikasi mobile bisa terpasang berbulan-bulan tanpa di-update, jadi ketika format response harus berubah total, Anda bisa menambah v2 sementara aplikasi versi lama tetap jalan di v1.
Pilih Mode Sanctum yang Tepat
| Client | Autentikasi | Catatan |
|---|---|---|
| Aplikasi mobile | Personal access token Sanctum | Buat token saat login, simpan di keychain atau keystore perangkat, dan beri ability serta masa berlaku |
| Frontend web di domain utama yang sama | Autentikasi SPA Sanctum dengan cookie | Memakai cookie session dan proteksi CSRF, jadi tidak ada token yang disimpan di JavaScript |
| Server lain atau sistem partner | Token dengan ability terbatas, atau Laravel Passport kalau partner butuh OAuth | Satu token per partner supaya masing-masing bisa dicabut sendiri-sendiri |
Hindari menyimpan token di localStorage browser. Script apa pun yang berhasil disisipkan ke halaman bisa membacanya. Untuk frontend web, autentikasi SPA berbasis cookie adalah pilihan yang lebih aman.
Validasi dengan Form Request
Pindahkan validasi dari controller ke class Form Request yang dibuat dengan php artisan make:request. Aturan validasi tinggal di satu tempat bersama method authorize, controller jadi pendek, dan class ini sekaligus jadi dokumentasi tentang input yang diterima endpoint. Kalau client mengirim header Accept: application/json, validasi yang gagal mengembalikan response 422 dengan objek errors per nama field, yang bisa langsung dipetakan developer frontend dan mobile ke field form masing-masing. Minta semua client mengirim header itu. Tanpa header itu, Laravel bisa membalas request yang gagal dengan redirect.
Atur Bentuk Response dengan API Resource
Mengembalikan model Eloquent secara langsung akan menampilkan semua kolom, termasuk kolom yang baru ditambahkan nanti, dan mengikat API ke struktur database. API Resource (php artisan make:resource) menentukan dengan jelas field apa saja yang keluar dan dengan nama apa. Anda bisa mengganti nama kolom tanpa mengubah API, menyembunyikan field internal, memformat tanggal di satu tempat, dan menyertakan relasi hanya kalau relasinya sudah di-load dengan whenLoaded. Collection resource juga otomatis menambahkan link dan metadata pagination.
Pakai Status Code dengan Konsisten
| Situasi | Status code |
|---|---|
| Baca atau update berhasil | 200 OK |
| Data baru berhasil dibuat | 201 Created |
| Hapus berhasil tanpa isi response | 204 No Content |
| Belum login atau token tidak valid | 401 Unauthorized |
| Sudah login tapi tidak punya hak akses | 403 Forbidden |
| Data tidak ditemukan | 404 Not Found |
| Validasi gagal | 422 Unprocessable Content |
| Request terlalu banyak | 429 Too Many Requests |
Pakai satu format error untuk semua yang bukan error validasi, misalnya field message dan field code opsional yang bisa dicek client. Di Laravel 11 ke atas, tampilan exception untuk route API bisa diatur di bootstrap/app.php. Pastikan stack trace tidak pernah sampai ke client dengan mematikan APP_DEBUG di production.
Cek Hak Akses di Setiap Endpoint
Bug serius yang paling sering kami temukan saat mereview API adalah endpoint yang mengecek user sudah login, tapi tidak mengecek apakah datanya milik user itu. Cukup mengganti ID di URL, pesanan pelanggan lain langsung terlihat. Buat Policy untuk setiap model dan panggil di setiap method controller atau Form Request. Tambahkan juga feature test yang meminta data milik user lain dan memastikan hasilnya 403 atau 404.
Pagination, Filter, dan Performa
- Jangan pernah mengembalikan list tanpa batas. Pakai paginate untuk halaman yang butuh total data, atau cursorPaginate untuk feed panjang dan infinite scroll karena tetap cepat di tabel besar.
- Tetapkan batas maksimal jumlah data per halaman di server supaya client tidak bisa meminta 100.000 baris sekaligus.
- Load relasi dengan with() dan aktifkan Model::preventLazyLoading() di luar production, supaya query N+1 langsung ketahuan saat development.
- Tambahkan index database untuk kolom yang dipakai di filter dan sorting.
- Cache response yang berat dan jarang berubah, seperti data referensi dan pengaturan.
Rate Limiting
Definisikan limiter dengan RateLimiter::for di service provider, lalu pasang dengan middleware throttle. Beri batas ketat per alamat IP untuk endpoint login, OTP, dan reset password, dan batas umum per user untuk endpoint lainnya. Client yang melewati batas akan menerima response 429 dengan header Retry-After, jadi aplikasi mobile bisa menunggu lalu mencoba lagi tanpa membanjiri server.
Dokumentasi dan Testing
- 1Buat dokumen OpenAPI langsung dari kode dengan package seperti Scramble, supaya dokumentasi selalu mengikuti Form Request dan Resource yang ada.
- 2Bagikan file OpenAPI ke tim frontend dan mobile supaya mereka bisa membuat client yang sudah bertipe.
- 3Tulis feature test dengan getJson dan postJson untuk setiap endpoint, mencakup kasus berhasil, error validasi, dan akses oleh user yang tidak berhak.
- 4Jalankan test di CI untuk setiap pull request, sekalian cek bahwa dokumen OpenAPI masih bisa dibuat.
Poin penting
- Beri versi pada API sejak awal, pakai token Sanctum untuk mobile dan cookie untuk web.
- Validasi dengan Form Request dan atur output dengan API Resource, jangan mengembalikan model langsung.
- Pakai status code yang konsisten dan satu format error, dengan debug mati di production.
- Cek kepemilikan data dengan Policy di setiap endpoint dan uji akses oleh user yang tidak berhak.
- Pakai pagination di semua list, cegah query N+1, batasi request di endpoint sensitif, dan buat dokumentasi OpenAPI dari kode.


