Tong Sampah Kategori: Satu Endpoint dan POST Restore
Kontrak API soft delete kategori: query flag trashed, POST restore, dan 404 yang sengaja menggabungkan dua kondisi.
Ringkasan
Daripada bikin endpoint sampah baru, daftar kategori admin sekarang cukup pakai query trashed yang defaultnya false jadi aplikasi lama aman aja. Kalau mau balikin data tinggal hit POST restore, kalau kategorinya nggak ada atau belum kehapus bakal dibales 404. Enaknya kontraknya aditif banget jadi frontend baru langsung jalan tanpa ngerusak yang lama.
Saya lagi ngecek diff OpenAPI untuk modul administrasi KotaPortal. Tiba-tiba muncul perubahan di skema kategori. Bukan sekadar nambah field biasa, tapi ada mekanisme soft delete yang baru.
Jujur, tebakan awal saya standar banget. Biasanya kalau mau bikin fitur tong sampah, kita langsung kepikiran bikin endpoint terpisah, misalnya GET /admin/categories/trash. Atau kalau mau restore, mungkin pakai DELETE ke resource sampah itu, atau PATCH buat ngubah statusnya balik ke aktif.
Ternyata diff-nya beda. Alih-alih nambah endpoint baru, kontraknya cuma nambah satu parameter query boolean bernama trashed di endpoint daftar kategori admin, dengan nilai default false. Nilai false kasih daftar kategori aktif seperti biasa. Nilai true cuma kategori yang udah di-soft delete.
Ini perubahan yang aditif. Di ekosistem seperti KotaPortal, bisa jadi ada aplikasi mobile versi lama atau panel admin legacy yang masih memanggil endpoint kategori tanpa parameter baru. Kalau kita bikin endpoint terpisah atau ngubah default behavior, kita langsung memecah kompatibilitas. Dengan default false, kontrak tetap aman. Konsumen lama tetap dapat data yang mereka harapkan, sementara fitur baru bisa langsung dipakai oleh frontend yang sudah diupdate.
Untuk restore, endpoint barunya adalah POST /admin/categories/{id}/restore. Response yang didokumentasikan cuma tiga: 200 dengan objek kecil berisi flag ok, 403 kalau di luar scope requester, dan 404 dengan deskripsi spesifik: "Category not found (or not trashed)".
paths:
/admin/categories:
get:
parameters:
- name: trashed
in: query
schema:
type: boolean
default: false
/admin/categories/{id}/restore:
post:
responses:
'200':
description: Restore result
'404':
description: Category not found (or not trashed)
'403':
description: Out of scope
Kenapa 404-nya Menggabungkan Dua Kondisi
Karena restore cuma berhasil kalau kategorinya emang ada di tempat sampah. Jadi, kategori nggak ada sama sekali dan kategori ada tapi nggak lagi di-trash itu sama-sama nggak punya representasi yang valid untuk di-restore. RFC 9110 dengan jelas bilang bahwa POST memproses representasi sesuai semantik resource itu sendiri, dan 404 berarti server nggak menemukan representasi terkini (atau nggak mau mengakuinya), tanpa menyatakan apakah kondisi itu sementara atau permanen [5].
Parameter Query di Mata Spesifikasi
Di spesifikasi OpenAPI, objek Parameter punya field penentu lokasi: query, header, path, atau cookie. Parameter path wajib punya tanda required, sementara yang lain default-nya opsional [6]. Ini bikin parameter trashed jadi opsional tapi punya default yang aman. Prinsip ini juga sejalan dengan konvensi bahwa field opsional nggak boleh diasumsikan ada oleh pembaca, dan default sebaiknya nempel di field opsional tersebut [3].
Kontrak-First Berarti Diff Spesifikasi = Diff Klien
Efek samping yang paling saya suka dari pendekatan kontrak-first ini: file definisi tipe yang digenerate oleh openapi-typescript langsung nangkep parameter baru dan operasi restore ini secara otomatis. Nggak perlu nulis manual atau ngecek ulang satu per satu.
Diff yang sama juga bawa tambahan skema entitas: contact_person jadi nullable, plus field rating_enabled, rental_enabled, dan type_id yang dipesan untuk tabel master masa depan. Semua ini mengikuti pola yang sama. Kita nggak menghapus atau ngubah tipe data lama, cuma nambah apa yang diperlukan.
Saya awalnya ragu sama deskripsi 404 yang menggabungkan dua kondisi itu. Tapi setelah dipikir lagi, ini justru keputusan kontrak yang solid. Kita nggak perlu bikin state machine yang rumit cuma buat bilang "eh, ini nggak lagi di-trash lho". Cukup tolak dengan 404, biarkan klien menangani logika UI-nya.