Foto oleh Leef Parks via Pexels

API versioning adalah praktik mengelola perubahan pada API seiring waktu tanpa merusak aplikasi client yang sudah ada dan bergantung pada versi API sebelumnya, menjadi krusial ketika API sudah dikonsumsi oleh banyak client yang tidak bisa di-update secara bersamaan.

Ada beberapa strategi umum untuk melakukan versioning: lewat URL path (/api/v1/produk), lewat query string (/api/produk?version=1), lewat header kustom, atau lewat Accept header dengan media type versioning, masing-masing memiliki trade-off tersendiri dari sisi kejelasan dan fleksibilitas.

Artikel ini membahas berbagai strategi versioning tersebut, implementasinya di ASP.NET Core, serta pertimbangan kapan sebaiknya membuat versi API baru dibanding sekadar menambahkan field baru yang bersifat backward-compatible.

Versioning Lewat URL Path

Versi API terlihat jelas langsung dari URL

GET /api/v1/produk GET /api/v2/produk // Implementasi di ASP.NET Core dengan Asp.Versioning package: [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/produk")] public class ProdukV1Controller : ControllerBase { }
Versioning lewat URL path adalah pendekatan paling umum dan paling mudah dipahami karena versi API terlihat jelas langsung dari URL itu sendiri, memudahkan debugging dan dokumentasi, meski secara teknis sedikit melanggar prinsip bahwa URL seharusnya merepresentasikan resource, bukan versi implementasi.

Versioning Lewat Header Kustom

Versi dikirim lewat header, URL tetap bersih

GET /api/produk X-Api-Version: 2 // Konfigurasi di ASP.NET Core: options.ApiVersionReader = new HeaderApiVersionReader("X-Api-Version");
Versioning lewat header kustom menjaga URL tetap bersih tanpa informasi versi di dalamnya, yang secara teori lebih sesuai dengan prinsip bahwa URL seharusnya hanya merepresentasikan resource, namun kurang terlihat jelas bagi developer yang hanya melihat URL tanpa memeriksa header request secara detail.

Menjaga Backward Compatibility Tanpa Versi Baru

Penambahan field aman, perubahan tipe atau penghapusan tidak

// Menambah field baru BIASANYA aman tanpa versi baru: { "id": 5, "nama": "Laptop", "stok": 10 } // field BARU: stok // Mengubah TIPE atau MENGHAPUS field BUTUH versi baru: { "harga": "8000000" } -> { "harga": 8000000 } // ubah string ke number
Menambahkan field baru ke response umumnya aman dan tidak memerlukan versi API baru karena client lama akan mengabaikan field yang tidak mereka kenali, namun mengubah tipe data field yang sudah ada atau menghapusnya sepenuhnya adalah perubahan breaking yang membutuhkan versi API baru agar tidak merusak client lama yang masih mengharapkan format sebelumnya.

Strategi Deprecation untuk Versi Lama

Menandai versi lama sebagai deprecated sebelum benar-benar dihapus

[ApiVersion("1.0", Deprecated = true)] [Route("api/v{version:apiVersion}/produk")] public class ProdukV1Controller : ControllerBase { } // Response akan menyertakan header: // Sunset: Sat, 31 Dec 2024 23:59:59 GMT
Menandai versi API lama sebagai deprecated (bukan langsung menghapusnya) memberikan waktu bagi konsumen API untuk bermigrasi ke versi baru, biasanya disertai header Sunset yang menandakan kapan versi tersebut akan benar-benar dihentikan, memberikan transisi yang lebih baik dibanding menghapus versi lama secara mendadak.

Strategi Migrasi antar Versi API Tanpa Mematikan Klien Lama

Memberi sinyal deprecation lewat response header sebelum versi lama dimatikan

// Header deprecation memberi tahu client bahwa v1 akan dihentikan GET /api/v1/produk // Response headers: // Deprecation: true // Sunset: Wed, 31 Dec 2026 23:59:59 GMT // Link: </api/v2/produk>; rel="successor-version" // Client lama tetap berjalan normal, hanya mendapat peringatan di header // bukan error, sehingga tidak ada downtime mendadak

Menambahkan versi baru pada API tidak berarti versi lama bisa langsung dihapus, karena masih ada klien produksi yang bergantung padanya, baik aplikasi mobile yang belum diupdate penggunanya maupun sistem pihak ketiga yang terintegrasi dengan v1. Header standar seperti Deprecation dan Sunset memungkinkan server memberi sinyal bahwa sebuah versi akan dihentikan pada tanggal tertentu, tanpa langsung mematikan layanan dan memutus integrasi yang sedang berjalan.

Strategi migrasi yang aman biasanya melibatkan periode transisi di mana v1 dan v2 berjalan berdampingan, disertai pemantauan log untuk melihat seberapa banyak traffic yang masih memakai versi lama. Jika traffic v1 sudah mendekati nol atau hanya berasal dari sistem internal yang bisa dikoordinasikan langsung, barulah versi tersebut aman dihentikan sesuai tanggal sunset yang sudah diumumkan sebelumnya.

Komunikasi perubahan juga perlu dituangkan dalam changelog API yang menjelaskan perbedaan konkret antar versi, bukan hanya nomor versi yang berubah. Tanpa changelog yang jelas, tim yang mengintegrasikan API akan kesulitan menilai apakah migrasi ke versi baru memerlukan perubahan kode pada sisi mereka atau cukup mengganti base URL saja.

Memilih Lokasi Versi API: URI Path, Header, atau Query Parameter

Empat lokasi umum untuk menyisipkan informasi versi pada request API

// 1. Versioning di URI path - paling umum dan mudah dibaca GET /api/v2/produk // 2. Versioning lewat custom header - URL tetap bersih GET /api/produk X-Api-Version: 2 // 3. Versioning lewat query parameter - fleksibel untuk testing manual GET /api/produk?version=2 // 4. Versioning lewat media type (Accept header) - paling "RESTful" GET /api/produk Accept: application/vnd.mpmfinance.v2+json

Versioning lewat URI path seperti /api/v2/produk paling banyak dipakai karena mudah dibaca, mudah diuji langsung lewat browser atau Postman, dan jelas terlihat pada log server tanpa perlu membuka detail header. Kelemahannya, pendekatan ini menganggap versi sebagai bagian dari identitas URL, padahal secara prinsip REST resource yang sama seharusnya memiliki satu URI meski representasinya berubah seiring versi. Perubahan versi mayor pada path URL juga memudahkan tim operasional memantau traffic tiap versi secara terpisah lewat log atau API gateway.

Versioning lewat custom header seperti X-Api-Version menjaga URL tetap bersih dan konsisten di semua versi, sehingga lebih sesuai dengan prinsip bahwa URI mengidentifikasi resource bukan versinya. Namun pendekatan ini membuat eksplorasi API lewat browser menjadi tidak praktis karena header custom tidak bisa diatur langsung dari address bar, dan tim yang mengintegrasikan API harus selalu ingat menyertakan header tersebut pada setiap request. Header custom juga lebih mudah disembunyikan dari cache publik dibanding path URL, karena banyak proxy cache memperlakukan URL berbeda sebagai resource berbeda.

Versioning lewat media type pada header Accept dianggap paling murni secara teori REST karena memakai mekanisme content negotiation yang memang sudah disediakan HTTP, namun implementasinya lebih rumit dan kurang familiar bagi banyak developer. Pada praktiknya, kombinasi URI path untuk versi mayor dan header untuk informasi tambahan sering dipilih sebagai titik tengah yang cukup jelas tanpa terlalu memaksakan kemurnian teori REST. Keputusan strategi versioning sebaiknya didokumentasikan sejak awal proyek, karena mengubahnya di tengah jalan memaksa seluruh konsumen API melakukan migrasi ulang.

Menghasilkan Dokumentasi Versi API Otomatis lewat OpenAPI

Dua dokumen Swagger terpisah untuk v1 dan v2 pada satu aplikasi

// Swashbuckle menghasilkan dokumen OpenAPI terpisah untuk tiap versi builder.Services.AddSwaggerGen(opt => { opt.SwaggerDoc("v1", new OpenApiInfo { Title = "Produk API", Version = "v1" }); opt.SwaggerDoc("v2", new OpenApiInfo { Title = "Produk API", Version = "v2" }); }); // Hasil: /swagger/v1/swagger.json dan /swagger/v2/swagger.json // Dokumentasi interaktif otomatis mengikuti setiap perubahan kontrak

Dokumentasi API yang ditulis manual sering tertinggal dari kode sebenarnya, karena developer lupa memperbarui dokumen setiap kali endpoint berubah. Tool seperti Swashbuckle pada ASP.NET Core menghasilkan dokumen OpenAPI secara otomatis dari atribut dan tipe data yang sudah ada di kode, sehingga dokumentasi selalu sinkron dengan kontrak API yang benar-benar berjalan.

Ketika API memiliki beberapa versi berjalan bersamaan, dokumen OpenAPI juga dipisah per versi agar konsumen tidak bingung membaca endpoint yang sudah tidak berlaku di versi yang mereka pakai. Halaman Swagger UI yang dihasilkan dari dokumen ini memungkinkan tim lain mencoba memanggil endpoint langsung dari browser tanpa perlu menyiapkan tool seperti Postman terlebih dahulu.

Kesalahan Umum yang Sering Terjadi

Membuat Versi Baru untuk Setiap Perubahan Kecil

Membuat versi API baru untuk setiap perubahan sekecil apa pun, termasuk yang sebenarnya backward-compatible seperti penambahan field opsional, menambah beban maintenance yang tidak perlu dengan terlalu banyak versi yang harus didukung secara bersamaan.

Menghapus Versi Lama Secara Mendadak Tanpa Pemberitahuan

Langsung menghapus versi API lama tanpa periode deprecation yang jelas bisa merusak aplikasi client yang belum sempat bermigrasi, menyebabkan gangguan layanan yang bisa dihindari dengan komunikasi dan transisi yang lebih terencana.

Tidak Mendokumentasikan Perbedaan Antar Versi dengan Jelas

Membuat versi baru tanpa dokumentasi yang jelas tentang apa saja yang berubah dari versi sebelumnya menyulitkan konsumen API memutuskan apakah dan kapan mereka perlu bermigrasi ke versi tersebut.

Kapan Pendekatan Ini Tidak Cocok Dipakai

Versioning lewat URL path memberikan kejelasan visual yang tinggi dan mudah dipahami, namun secara teknis menambahkan informasi non-resource ke dalam URL yang idealnya hanya merepresentasikan resource menurut prinsip REST murni.

Versioning lewat header menjaga kebersihan URL, namun kurang terlihat jelas dan membutuhkan dokumentasi tambahan agar konsumen API memahami bahwa versi dikontrol lewat header, bukan sesuatu yang langsung terlihat hanya dengan melihat endpoint yang dipanggil.

Ringkasan

API versioning memastikan evolusi API bisa terus berjalan tanpa merusak aplikasi client yang sudah ada, dengan berbagai strategi seperti URL path atau header yang masing-masing memiliki trade-off tersendiri. Buat versi baru hanya untuk perubahan breaking yang benar-benar diperlukan, dan selalu berikan periode deprecation yang jelas sebelum menghentikan dukungan versi lama.

Seri Belajar REST API & Clean Architecture:

← Sebelumnya: Desain Endpoint REST API yang Baik

→ Selanjutnya: Pagination Filtering dan Sorting pada REST API