Membuat API yang berfungsi itu mudah; membuat API yang nyaman dipakai, aman, dan mudah dikembangkan itu soal disiplin. Berikut kumpulan praktik terbaik untuk API Laravel yang sudah kamu kuasai dasarnya (Membuat REST API dengan Laravel).
1. Rancang URL dengan konsisten
- Gunakan kata benda jamak, bukan kata kerja:
/api/produk, bukan/api/getProduk. - Hierarki untuk relasi:
/api/pelanggan/5/pesanan. - Huruf kecil, pemisah tanda hubung bila perlu (
/api/kategori-produk). - Tindakan non-CRUD tetap sebagai sub-resource:
POST /api/pesanan/12/pembatalan.
2. Versioning dari hari pertama
Begitu klien (aplikasi mobile) memakai APInya, mengubah format respons bisa merusak mereka. Beri versi:
Route::prefix('v1')->group(function () {
Route::apiResource('produk', ProdukController::class);
});
Perubahan yang merusak (menghapus field, mengganti tipe) dilakukan di v2, sementara v1 tetap hidup selama masa transisi.
3. Gunakan API Resource untuk membentuk respons
Jangan return model langsung. Resource memberi kontrol penuh atas field, format tanggal, dan relasi, serta mencegah kebocoran kolom internal.
return new ProdukResource($produk->load('kategori'));
Muat relasi hanya jika diminta (whenLoaded) agar tidak terjadi N+1.
4. Pagination, filter, dan sorting
Jangan pernah mengembalikan seluruh tabel.
$produk = Produk::query()
->when($request->kategori, fn ($q, $v) => $q->where('kategori_id', $v))
->when($request->q, fn ($q, $v) => $q->where('nama', 'like', "%{$v}%"))
->orderBy($request->input('sort', 'id'), $request->input('dir', 'desc'))
->paginate(min($request->integer('per_page', 15), 100));
Dua hal penting: batasi per_page maksimum, dan whitelist kolom sorting agar pengguna tidak bisa mengurutkan berdasarkan kolom sembarang. Untuk data yang sangat besar, cursorPaginate() lebih efisien.
5. Format error yang seragam
Klien harus bisa menangani semua error dengan cara yang sama. Contoh bentuk standar:
{
"message": "Data tidak valid.",
"errors": { "harga": ["Harga wajib diisi."] }
}
Laravel sudah menghasilkan format ini untuk validasi (422). Untuk error lain, atur di app/Exceptions/Handler.php agar permintaan JSON selalu mengembalikan JSON, termasuk 404, 403, dan 500, dengan kode status yang tepat. Pada produksi, jangan pernah mengirim stack trace ke klien.
6. Validasi dengan Form Request
Pindahkan validasi dan otorisasi dari controller ke class khusus:
php artisan make:request StoreProdukRequest
public function rules(): array
{
return [
'nama' => ['required', 'string', 'max:255'],
'harga' => ['required', 'numeric', 'min:0'],
'kategori_id' => ['required', 'exists:kategori,id'],
];
}
Controller menjadi ramping dan aturan bisa dipakai ulang serta diuji.
7. Autentikasi dan otorisasi
- Sanctum cocok untuk SPA dan aplikasi mobile; Passport jika butuh OAuth2 penuh.
- Beri token izin terbatas (abilities), dan buat token kedaluwarsa.
- Pisahkan autentikasi (siapa kamu) dari otorisasi (apa yang boleh kamu lakukan). Gunakan Policy:
$this->authorize('update', $produk); // 403 jika bukan pemilik
- Wajibkan HTTPS di produksi.
- Jangan pernah mempercayai ID dari klien tanpa memeriksa kepemilikan (cegah IDOR: pengguna A membaca data pengguna B dengan mengganti ID).
8. Rate limiting
Lindungi dari penyalahgunaan dan brute force:
// app/Providers/RouteServiceProvider.php
RateLimiter::for('api', fn (Request $r) =>
Limit::perMinute(60)->by($r->user()?->id ?: $r->ip()));
Endpoint sensitif seperti login diberi batas lebih ketat (misalnya 5 per menit). Respons 429 menyertakan header Retry-After.
9. Idempotensi dan konkurensi
GET,PUT,DELETEharus idempoten: dipanggil berulang hasilnya sama.- Untuk
POSTyang berisiko ganda (pembayaran), dukung headerIdempotency-Key. - Gunakan transaksi database untuk operasi multi-langkah, dan penguncian (
lockForUpdate) pada stok atau saldo.
DB::transaction(function () use ($data) {
$stok = Stok::lockForUpdate()->find($data['id']);
// kurangi stok, buat pesanan...
});
10. Performa
- Eager loading (
with) untuk mencegah N+1. Lihat Tips Optimasi Query Laravel. - Cache respons atau query yang mahal:
Cache::remember('kategori', 3600, fn () => ...). - Dukung ETag / Cache-Control untuk data yang jarang berubah.
- Pekerjaan berat (email, laporan) lewat queue, kembalikan 202 Accepted.
- Aktifkan kompresi gzip/brotli di web server.
11. Dokumentasi dan kontrak
API tanpa dokumentasi sulit dipakai. Hasilkan spesifikasi OpenAPI (Swagger) dengan paket seperti Scribe atau L5-Swagger, dan sediakan contoh permintaan/respons. Dokumentasi yang selalu sinkron dengan kode jauh lebih berharga daripada dokumen manual yang cepat usang.
12. Testing
public function test_membuat_produk_mengembalikan_201(): void
{
$user = User::factory()->create();
$this->actingAs($user, 'sanctum')
->postJson('/api/v1/produk', ['nama' => 'Pensil', 'harga' => 2500])
->assertCreated()
->assertJsonPath('data.nama', 'Pensil');
}
Uji jalur sukses, validasi gagal (422), tanpa login (401), tanpa izin (403), dan tidak ditemukan (404).
13. Observabilitas
- Catat log terstruktur dengan ID permintaan (correlation ID) agar bisa dilacak.
- Pantau waktu respons, tingkat error, dan query lambat.
- Jangan pernah mencatat password, token, atau data kartu.
Daftar periksa sebelum rilis
- [ ] URL konsisten dan diberi versi.
- [ ] Respons memakai Resource; tidak ada kolom sensitif.
- [ ] Pagination dengan batas maksimum.
- [ ] Error seragam dengan kode status benar.
- [ ] Validasi lewat Form Request.
- [ ] Autentikasi, policy, dan HTTPS.
- [ ] Rate limiting aktif.
- [ ] Tidak ada N+1; query lambat sudah diperiksa.
- [ ] Dokumentasi OpenAPI dan test otomatis.
Kesimpulan
API yang baik adalah API yang konsisten, aman, terdokumentasi, dan tidak mengejutkan klien. Terapkan praktik di atas sejak awal, karena memperbaikinya setelah banyak klien terlanjur bergantung jauh lebih mahal.
Komentar (0)
Masuk / Daftar