Aplikasi modern jarang hanya memiliki satu wajah: ada aplikasi web, ada aplikasi mobile, dan kadang ada mitra yang menempel pada endpoint kita. Jika API dibangun mengejar kebutuhan web saja, klien lain terpaksa melakukan trik aneh di atas respons yang tidak pernah dirancang untuk mereka. API-first development membalik urutannya: kontrak API dirancang, direview, dan disepakati lebih dulu — sebelum satu baris UI atau logika backend ditulis. Pada chapter ini kita akan menelusuri alur kerja spec-first, memanfaatkan mock server, menjaga disiplin versioning sejak hari pertama, dan menulis validator yang menjaga bentuk kontrak tetap sehat.
Kontrak Lebih Dulu, Kode Belakangan #
Dalam pendekatan code-first, kita menulis endpoint dulu lalu mendokumentasikannya setelah selesai — dokumentasi menjadi barang mewah yang cepat basi. API-first membalik alurnya menjadi lima langkah yang disiplin:
- Rancang kontrak lengkap di berkas spesifikasi (biasanya OpenAPI).
- Review spesifikasi tersebut bersama semua konsumen: tim web, tim mobile, tim mitra.
- Jalankan mock server dari spesifikasi agar frontend bisa mulai bekerja.
- Bangun backend dan frontend secara paralel terhadap kontrak yang sama.
- Uji implementasi terhadap kontrak (contract testing) sebelum rilis.
Spesifikasi menjadi single source of truth: jika ada pertanyaan “field hargaCents bertipe apa?”, jawabannya ada di spesifikasi, bukan di kepala seorang programmer. Kita sudah mengenal bentuk detail berkas OpenAPI pada chapter OpenAPI Documentation
; sekarang kita fokus pada alur kerjanya.
| Manfaat | Penjelasan | Dampak |
|---|---|---|
| Kerja paralel | Frontend memakai mock server, backend mengimplementasi kontrak | Timeline frontend dan backend tidak saling menunggu |
| Kejelasan kontrak | Semua pihak sepakat bentuk respons sebelum kode ada | Perdebatan desain terjadi di spesifikasi, bukan di kode setengah jadi |
| Deteksi breaking change | Diff spesifikasi memperlihatkan perubahan yang merusak klien | Perubahan berbahaya ketahuan sebelum rilis |
| Disiplin versioning | Versi menjadi bagian desain, bukan tambalan belakangan | Klien lama tidak ikut rusak saat API berkembang |
Spesifikasi sebagai Sumber Kebenaran #
Mari kita lihat potongan spesifikasi untuk layanan katalog. Perhatikan bahwa endpoint ini bisa direview oleh tim mobile dan tim mitra tanpa satu baris PHP pun ditulis:
openapi: 3.1.0
info:
title: Katalog API
version: 1.0.0
paths:
/v1/products/{sku}:
get:
operationId: getProduct
summary: Ambil detail satu produk
parameters:
- name: sku
in: path
required: true
schema:
type: string
responses:
"200":
description: Detail produk ditemukan
content:
application/json:
schema:
$ref: "#/components/schemas/Product"
"404":
description: Produk tidak ditemukan
components:
schemas:
Product:
type: object
required: [sku, nama, hargaCents]
properties:
sku: { type: string }
nama: { type: string }
hargaCents: { type: integer, minimum: 0 }Ada tiga keputusan desain penting yang tertanam di sini. Pertama, operationId: getProduct memberi nama operasi yang stabil untuk dirujuk kode dan pengujian. Kedua, respons sukses dan gagal (200 dan 404) didefinisikan eksplisit sehingga konsumen tahu apa yang harus diantisipasi. Ketiga, harga disepakati sebagai integer hargaCents — keputusan kecil yang menyelamatkan semua klien dari masalah presisi floating point, dan jauh lebih murah diambil sebelum kode ditulis daripada sesudahnya.
Mock Server dari Spesifikasi #
Karena kontrak sudah lengkap, frontend tidak perlu menunggu backend selesai. Mock server membaca spesifikasi lalu menyajikan respons palsu yang bentuknya persis mengikuti skema — permintaan GET /v1/products/PHP-101 dijawab dengan JSON contoh yang valid menurut kontrak. Tim frontend membangun seluruh alur layar, binding data, dan penanganan error terhadap mock tersebut; ketika backend asli siap, pergantian URL saja cukup.
Mock juga menuntut spesifikasi jujur. Jika sebuah field tidak tercantum di skema, mock tidak akan pernah mengirimkannya — field “yang sebenarnya ada kok di backend” tidak akan lolos ke layar pengguna. Umpan balik semacam ini biasanya memaksa spesifikasi diperbaiki di minggu pertama, saat murah, bukan di minggu rilis.
Validator Bentuk Kontrak #
Spesifikasi hanya bisa jadi sumber kebenaran jika kualitasnya dijaga. Kesalahan umum menyusun spesifikasi antara lain: lupa operationId, tidak mendefinisikan respons error, atau menambahkan endpoint baru tanpa prefix versi. Kita bisa menulis validator yang memeriksa aturan-aturan tersebut pada struktur data kontrak — dan struktur seperti ini persis yang dihasilkan ketika berkas OpenAPI di-parse menjadi array PHP:
<?php
final class ContractValidator
{
/**
* @param array<string, array{operationId?: string, responses?: array<string, mixed>}> $operations
* @return list<string>
*/
public function validate(array $operations): array
{
$violations = [];
foreach ($operations as $key => $operation) {
[$method, $path] = explode(' ', $key);
if (!isset($operation['operationId'])) {
$violations[] = "$key: wajib memiliki operationId";
}
$responses = $operation['responses'] ?? [];
if (!isset($responses['200'], $responses['404'])) {
$violations[] = "$key: wajib mendefinisikan respons 200 dan 404";
}
if (!str_starts_with($path, '/v1/')) {
$violations[] = "$key: path harus diawali /v1/";
}
}
return $violations;
}
}
$kontrak = [
'GET /v1/products' => [
'operationId' => 'listProducts',
'responses' => ['200' => ['application/json'], '404' => []],
],
'GET /v1/products/{sku}' => [
'operationId' => 'getProduct',
'responses' => ['200' => ['application/json']],
],
'POST /orders' => [
'responses' => [],
],
];
$validator = new ContractValidator();
foreach ($validator->validate($kontrak) as $violation) {
echo "- $violation\n";
}Output yang Dihasilkan:
- GET /v1/products/{sku}: wajib mendefinisikan respons 200 dan 404
- POST /orders: wajib memiliki operationId
- POST /orders: wajib mendefinisikan respons 200 dan 404
- POST /orders: path harus diawali /v1/Mari kita bedah cara kerja validator di atas:
explode(' ', $key): setiap operasi dilabeli gabungan"METHOD /path", dipisah menjadi metode dan path untuk pemeriksaan berbeda.- Pemeriksaan
operationId:isset()pada kunci array mendeteksi label yang hilang tanpa perlu null check tambahan. - Pemeriksaan respons:
isset($responses['200'], $responses['404'])hanya bernilaitruejika kedua kunci ada — satuisset()untuk dua syarat sekaligus. - Pemeriksaan prefix:
str_starts_with($path, '/v1/')memastikan disiplin versioning (yang akan kita bahas sebentar lagi) tidak terlewat pada endpoint baru.
Perhatikan hasilnya: GET /v1/products lolos semua aturan, GET /v1/products/{sku} tertangkap karena 404 tidak didefinisikan, dan POST /orders melanggar ketiga aturan sekaligus. Dalam proyek nyata, validator seperti ini dijalankan terhadap berkas OpenAPI yang sudah di-parse (misalnya hasil json_decode() pada berkas JSON yang juga divalidasi dengan json_validate()
), lalu dijalankan di CI.
Disiplin Versioning Sejak Hari Pertama #
Aturan prefix /v1/ pada validator di atas bukan hiasan. Perubahan API yang tidak kompatibel — menghapus field, mengganti tipe, mengubah struktur respons — akan merusak klien yang tidak ikut diperbarui. Dengan prefix versi di path, versi baru bisa hidup berdampingan dengan versi lama: /v1/products tetap melayani klien lama sementara /v2/products memperkenalkan bentuk baru. Strategi lengkapnya (path, header, kapan menaikkan versi) sudah kita bahas pada chapter API Versioning
; di sini cukup satu prinsip: versi adalah keputusan desain, bukan tambalan darurat setelah klien rusak.
API sebagai Produk #
Pola pikir penutup dari API-first: perlakukan API sebagai produk, bukan sebagai efek samping backend. Konsumen API — tim mobile, tim web, tim mitra — adalah pelanggan produk tersebut. Kebutuhan mereka didengar saat review spesifikasi, perubahan diumumkan lewat changelog, dan kompatibilitas lama dijaga seperti layaknya janji kepada pelanggan. Tim yang memandang API demikian cenderung menghasilkan endpoint yang konsisten, terdokumentasi, dan bertahan lama; tim yang memandang API sebagai detail internal cenderung menghasilkan kontrak yang berubah-ubah mengikuti kebutuhan implementasi hari itu.
Ringkasan #
API-first development menempatkan kontrak di depan: spesifikasi OpenAPI dirancang dan direview lebih dulu, mock server menghidupkannya untuk frontend, backend dan frontend bekerja paralel, dan validator otomatis menjaga bentuk kontrak tetap sehat dari hari pertama. Manfaat terbesarnya bukan kecepatan semata, melainkan kejelasan — semua pihak bekerja menghadapi kebenaran yang sama. Namun makin banyak jenis klien yang dilayani, makin terasa bahwa satu kontrak seragam tetap tidak cukup: web dan mobile punya kebutuhan yang berbeda-beda. Di situlah pola Backend-for-Frontend masuk, yang menjadi pembahasan chapter berikutnya.