Desain API web RESTful

Sebagian besar aplikasi web modern mengekspos API yang dapat digunakan klien untuk berinteraksi dengan aplikasi. API web yang dirancang dengan baik harus bertujuan untuk mendukung:

  • Kemandirian platform. Setiap klien harus dapat memanggil API, terlepas dari cara API diterapkan secara internal. Hal ini membutuhkan penggunaan protokol standar, dan memiliki mekanisme di mana klien dan layanan web dapat menyetujui format data yang akan dipertukarkan.

  • Evolusi layanan. API web harus dapat berkembang dan menambahkan fungsionalitas secara independen dari aplikasi klien. Seiring berkembangnya API, aplikasi klien yang ada harus terus berfungsi tanpa perubahan. Semua fungsionalitas harus dapat ditemukan sehingga aplikasi klien dapat menggunakannya sepenuhnya.

Panduan ini menjelaskan masalah yang harus Anda pertimbangkan saat mendesain API web.

Apa itu REST?

Pada tahun 2000, Roy Fielding mengusulkan Representational State Transfer (REST) sebagai pendekatan arsitektur untuk merancang layanan web. REST adalah gaya arsitektur untuk membangun sistem terdistribusi berdasarkan hypermedia. REST tidak bergantung pada protokol yang mendasari dan tidak harus terikat dengan HTTP. Tetapi, penerapan REST API yang paling umum menggunakan HTTP sebagai protokol aplikasi, dan panduan ini berfokus pada perancangan REST API untuk HTTP.

Keuntungan utama REST dibandingkan HTTP adalah REST menggunakan standar terbuka, dan tidak mengikat penerapan API atau aplikasi klien ke penerapan spesifik apa pun. Misalnya, layanan web REST dapat ditulis dalam ASP.NET, dan aplikasi klien dapat menggunakan bahasa atau alat apa pun yang dapat menghasilkan permintaan HTTP dan mengurai respons HTTP.

Berikut adalah beberapa prinsip desain utama RESTful API menggunakan HTTP:

  • REST API dirancang berdasarkan sumber daya, yang merupakan jenis objek, data, atau layanan apa pun yang dapat diakses oleh klien.

  • Sumber daya memiliki pengidentifikasi, yang merupakan URI yang secara unik mengidentifikasi sumber daya tersebut. Misalnya, URI untuk pesanan pelanggan tertentu mungkin berupa:

    https://adventure-works.com/orders/1
    
  • Klien berinteraksi dengan layanan dengan bertukar representasi sumber daya. Banyak API web menggunakan JSON sebagai format pertukaran. Misalnya, permintaan GET ke URI yang tercantum di atas mungkin menampilkan isi respons ini:

    {"orderId":1,"orderValue":99.90,"productId":1,"quantity":1}
    
  • REST API menggunakan antarmuka yang seragam, yang membantu memisahkan penerapan klien dan layanan. Untuk REST API yang dibangun di atas HTTP, antarmuka yang seragam mencakup penggunaan kata kerja HTTP standar untuk melakukan operasi pada sumber daya. Operasi yang paling umum adalah GET, POST, PUT, PATCH, dan DELETE.

  • REST API menggunakan model permintaan tanpa status. Permintaan HTTP harus independen dan mungkin terjadi dalam urutan apa pun, sehingga menjaga informasi status sementara antar permintaan tidak layak. Satu-satunya tempat di mana informasi disimpan adalah di sumber daya itu sendiri, dan setiap permintaan harus berupa operasi atomik. Batasan ini memungkinkan layanan web menjadi dapat diskalakan dengan mudah, karena tidak perlu mempertahankan afinitas apa pun antara klien dan server tertentu. Server mana pun dapat menangani semua permintaan dari klien. Oleh karena itu, faktor lain dapat membatasi skalabilitas. Misalnya, banyak layanan web menulis ke penyimpanan data backend, yang mungkin sulit untuk diskalakan. Untuk informasi selengkapnya tentang strategi untuk memperluas skala penyimpanan data, lihat Pemartisian data horizontal, vertikal, dan fungsional.

  • REST API didorong oleh tautan hypermedia yang terdapat dalam representasi. Misalnya, berikut ini menunjukkan representasi JSON dari pesanan. JSON berisi tautan untuk mendapatkan atau memperbarui pelanggan yang terkait dengan pesanan.

    {
      "orderID":3,
      "productID":2,
      "quantity":4,
      "orderValue":16.60,
      "links": [
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"GET" },
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"PUT" }
      ]
    }
    

Pada tahun 2008, Leonard Richardson mengusulkan model kematangan berikut untuk API web:

  • Tingkat 0: Menentukan satu URI, dan semua operasi adalah permintaan POST ke URI ini.
  • Tingkat 1: Membuat URI terpisah untuk masing-masing sumber daya.
  • Tingkat 2: Menggunakan metode HTTP untuk menentukan operasi pada sumber daya.
  • Tingkat 3: Menggunakan hypermedia (HATEOAS, dijelaskan di bawah).

Tingkat 3 sesuai dengan RESTful API berdasarkan definisi Fielding. Dalam praktiknya, banyak API web yang diterbitkan berada di sekitar tingkat 2.

Mengatur desain API di sekitar sumber daya

Fokus pada entitas bisnis yang diperlihatkan oleh API web. Misalnya, dalam sistem e-niaga, entitas utama mungkin adalah pelanggan dan pesanan. Membuat pesanan dapat dilakukan dengan mengirimkan permintaan HTTP POST yang berisi informasi pesanan. Respons HTTP menunjukkan apakah pesanan berhasil dilakukan atau tidak. Jika memungkinkan, URI sumber daya harus didasarkan pada kata benda (sumber daya) dan bukan kata kerja (operasi pada sumber daya).

https://adventure-works.com/orders // Good

https://adventure-works.com/create-order // Avoid

Sumber daya tidak harus didasarkan pada satu item data fisik. Misalnya, sumber daya pesanan mungkin diterapkan secara internal sebagai beberapa tabel dalam database hubungan, tetapi disajikan kepada klien sebagai satu entitas. Hindari membuat API yang hanya mencerminkan struktur internal database. Tujuan REST adalah untuk memodelkan entitas dan operasi yang dapat dilakukan aplikasi pada entitas tersebut. Klien tidak boleh terekspos ke penerapan internal.

Entitas sering dikelompokkan bersama ke dalam kumpulan (pesanan, pelanggan). Kumpulan adalah sumber daya terpisah dari item dalam kumpulan, dan harus memiliki URI sendiri. Misalnya, URI berikut mungkin mewakili kumpulan pesanan:

https://adventure-works.com/orders

Mengirim permintaan HTTP GET ke kumpulan URI akan mengambil daftar item dalam kumpulan. Setiap item dalam kumpulan juga memiliki URI uniknya sendiri. Permintaan HTTP GET ke URI item mengembalikan detail item tersebut.

Gunakan konvensi penamaan yang konsisten di URI. Secara umum, akan membantu jika menggunakan kata benda jamak untuk URI yang mereferensikan kumpulan. Ini adalah praktik yang baik untuk mengatur URI untuk kumpulan dan item ke dalam hierarki. Misalnya, /customers adalah jalur ke kumpulan pelanggan, dan /customers/5 adalah jalur ke pelanggan dengan ID sama dengan 5. Pendekatan ini membantu menjaga API web tetap intuitif. Selain itu, banyak kerangka kerja API web dapat merutekan permintaan berdasarkan jalur URI berparameter, sehingga Anda dapat menentukan rute untuk jalur /customers/{id}.

Selain itu, pertimbangkan hubungan antara berbagai jenis sumber daya dan cara Anda dapat mengekspos asosiasi ini. Misalnya, /customers/5/orders mungkin mewakili semua pesanan untuk pelanggan 5. Anda juga dapat pergi ke arah lain, dan mewakili asosiasi dari pesanan kembali ke pelanggan dengan URI seperti /orders/99/customer. Tetapi, memperluas model ini secara berlebihan dapat menjadi rumit untuk diterapkan. Solusi yang lebih baik adalah menyediakan tautan yang dapat dinavigasi ke sumber daya terkait di isi pesan respons HTTP. Mekanisme ini dijelaskan lebih rinci di bagian Menggunakan HATEOAS untuk mengaktifkan navigasi ke sumber daya terkait.

Dalam sistem yang lebih kompleks, mungkin ada godaan untuk menyediakan URI yang memungkinkan klien menavigasi melalui beberapa tingkat hubungan, seperti /customers/1/orders/99/products. Tetapi, tingkat kompleksitas ini dapat menjadi sulit dipertahankan dan tidak fleksibel jika hubungan antara sumber daya berubah di masa depan. Sebaliknya, coba untuk menjaga URI tetap sederhana. Setelah aplikasi memiliki referensi ke sumber daya, referensi ini seharusnya dapat digunakan untuk menemukan item yang terkait dengan sumber daya tersebut. Kueri sebelumnya dapat diganti dengan URI /customers/1/orders untuk menemukan semua pesanan untuk pelanggan 1, lalu /orders/99/products untuk menemukan produk dalam pesanan ini.

Tip

Hindari penggunaan URI sumber daya yang lebih kompleks daripada collection/item/collection.

Faktor lainnya adalah bahwa semua permintaan web membebani server web. Semakin banyak permintaan, semakin besar bebannya. Oleh karena itu, hindari API web "rumit" yang mengekspos sejumlah besar sumber daya kecil. API seperti itu mungkin memerlukan aplikasi klien untuk mengirim beberapa permintaan untuk menemukan semua data yang diperlukan. Sebagai gantinya, Anda mungkin perlu melakukan denormalisasi data dan menggabungkan informasi terkait menjadi sumber daya yang lebih besar yang dapat diambil dengan satu permintaan. Tetapi, Anda perlu menyeimbangkan pendekatan ini dengan biaya data pengambilan yang tidak dibutuhkan klien. Mengambil objek besar dapat meningkatkan latensi permintaan dan menimbulkan biaya bandwidth tambahan. Untuk informasi selengkapnya tentang antipola performa ini, lihat I/O Rumit dan Pengambilan Ekstra.

Hindari memperkenalkan dependensi antara API web dan sumber data yang mendasari. Misalnya, jika data Anda disimpan dalam database hubungan, API web tidak perlu mengekspos setiap tabel sebagai kumpulan sumber daya. Bahkan, hal ini mungkin merupakan desain yang buruk. Sebaliknya, anggap API web sebagai abstraksi dari database. Jika perlu, gunakan lapisan pemetaan antara database dan API web. Dengan begitu, aplikasi klien diisolasi dari perubahan skema database yang mendasari.

Terakhir, mungkin tidak mungkin untuk memetakan setiap operasi yang diterapkan oleh API web ke sumber daya tertentu. Anda dapat menangani skenario non-sumber daya tersebut melalui permintaan HTTP yang memanggil fungsi dan mengembalikan hasilnya sebagai pesan respons HTTP. Misalnya, API web yang menerapkan operasi kalkulator sederhana seperti penambahan dan pengurangan dapat menyediakan URI yang mengekspos operasi ini sebagai sumber daya semu dan menggunakan string kueri untuk menentukan parameter yang diperlukan. Misalnya, permintaan GET ke URI /add?operand1=99&operand2=1 akan mengembalikan pesan respons dengan isi yang berisi nilai 100. Tetapi, hanya gunakan bentuk URI ini dengan hemat.

Menentukan operasi API terkait metode HTTP

Protokol HTTP menentukan sejumlah metode yang menetapkan makna semantik untuk permintaan. Metode HTTP umum yang digunakan oleh sebagian besar API web RESTful adalah:

  • GET mengambil representasi sumber daya di URI yang ditentukan. Isi pesan respons berisi detail sumber daya yang diminta.
  • POST membuat sumber daya baru di URI yang ditentukan. Isi pesan permintaan memberikan detail sumber daya baru. Perhatikan bahwa POST juga dapat digunakan untuk memicu operasi yang tidak benar-benar membuat sumber daya.
  • PUT membuat atau mengganti sumber daya pada URI yang ditentukan. Isi pesan permintaan menentukan sumber daya yang akan dibuat atau diperbarui.
  • PATCH melakukan pembaruan sebagian sumber daya. Isi permintaan menentukan serangkaian perubahan untuk diterapkan ke sumber daya.
  • DELETE menghapus sumber daya pada URI yang ditentukan.

Efek dari permintaan khusus harus bergantung pada apakah sumber daya adalah kumpulan atau item terpisah. Tabel berikut merangkum konvensi umum yang diadopsi oleh sebagian besar penerapan RESTful menggunakan contoh e-niaga. Tidak semua permintaan ini dapat diterapkanā€”bergantung pada skenario tertentu.

Sumber daya POST DAPATKAN TARUH DELETE
/customers Membuat pelanggan baru Mengambil semua pelanggan Pembaruan massal pelanggan Menghapus semua pelanggan
/customers/1 Kesalahan Mengambil detail untuk pelanggan 1 Memperbarui detail pelanggan 1 jika ada Menghapus pelanggan 1
/customers/1/orders Membuat pesanan baru untuk pelanggan 1 Mengambil semua pesanan untuk pelanggan 1 Pembaruan massal pesanan untuk pelanggan 1 Menghapus semua pesanan untuk pelanggan 1

Perbedaan antara POST, PUT, dan PATCH dapat membingungkan.

  • Permintaan POST membuat sumber daya. Server menetapkan URI untuk sumber daya baru, dan mengembalikan URI tersebut ke klien. Dalam model REST, Anda sering menerapkan permintaan POST ke kumpulan. Sumber daya baru ditambahkan ke kumpulan. Permintaan POST juga dapat digunakan untuk mengirimkan data untuk diproses ke sumber daya yang ada, tanpa ada sumber daya baru yang dibuat.

  • Permintaan PUT membuat sumber daya atau memperbarui sumber daya yang ada. Klien menentukan URI untuk sumber daya. Isi permintaan berisi representasi lengkap dari sumber daya. Jika sumber daya dengan URI ini sudah ada, sumber daya akan diganti. Jika tidak, sumber daya baru akan dibuat, jika server mendukungnya. Permintaan PUT paling sering diterapkan ke sumber daya yang merupakan item terpisah, seperti pelanggan tertentu, daripada kumpulan. Server mungkin mendukung pembaruan tetapi tidak mendukung pembuatan melalui PUT. Dukungan pembuatan melalui PUT bergantung pada apakah klien dapat menetapkan URI ke sumber daya sebelum sumber daya ada. Jika tidak, gunakan POST untuk membuat sumber daya dan PUT atau PATCH untuk memperbarui.

  • Permintaan PATCH melakukan pembaruan parsial ke sumber daya yang ada. Klien menentukan URI untuk sumber daya. Isi permintaan menetapkan serangkaian perubahan untuk diterapkan pada sumber daya. Hal ini lebih efisien daripada menggunakan PUT, karena klien hanya mengirim perubahan, bukan seluruh representasi sumber daya. Secara teknis PATCH juga dapat membuat sumber daya baru (dengan menetapkan serangkaian pembaruan ke sumber daya "null"), jika server mendukung ini.

Permintaan PUT harus idempoten. Jika klien mengirimkan permintaan PUT yang sama beberapa kali, hasilnya harus selalu sama (sumber daya yang sama akan diubah dengan nilai yang sama). Permintaan POST dan PATCH tidak dijamin idempoten.

Sesuai dengan semantik HTTP

Bagian ini menjelaskan beberapa pertimbangan umum untuk merancang API yang sesuai dengan spesifikasi HTTP. Tetapi, bagian ini tidak mencakup setiap detail atau skenario yang mungkin terjadi. Jika ragu, lihat spesifikasi HTTP.

Jenis media

Seperti disebutkan sebelumnya, klien dan server bertukar representasi sumber daya. Misalnya, dalam permintaan POST, isi permintaan berisi representasi sumber daya yang akan dibuat. Dalam permintaan GET, isi respons berisi representasi dari sumber daya yang diambil.

Dalam protokol HTTP, format ditentukan melalui penggunaan jenis media, juga disebut jenis MIME. Untuk data non-biner, sebagian besar API web mendukung JSON (jenis media = application/json) dan mungkin XML (jenis media = application/xml).

Header Content-Type dalam permintaan atau respons menentukan format representasi. Berikut adalah contoh permintaan POST yang menyertakan data JSON:

POST https://adventure-works.com/orders HTTP/1.1
Content-Type: application/json; charset=utf-8
Content-Length: 57

{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

Jika server tidak mendukung jenis media, server harus menampilkan kode status HTTP 415 (Jenis Media Tidak Didukung).

Permintaan klien dapat menyertakan header Terima yang berisi daftar jenis media yang akan diterima klien dari server dalam pesan respons. Contohnya:

GET https://adventure-works.com/orders/2 HTTP/1.1
Accept: application/json

Jika server tidak dapat mencocokkan salah satu jenis media yang tercantum, server harus mengembalikan kode status HTTP 406 (Tidak Dapat Diterima).

Metode GET

Metode GET yang berhasil biasanya menampilkan kode status HTTP 200 (OK). Jika sumber daya tidak dapat ditemukan, metode harus menampilkan 404 (Tidak Ditemukan).

Jika permintaan terpenuhi tetapi tidak ada isi respons yang disertakan dalam respons HTTP, maka permintaan harus mengembalikan kode status HTTP 204 (Tidak Ada Konten); misalnya, operasi pencarian yang menghasilkan tidak ada kecocokan yang mungkin diimplementasikan dengan perilaku ini.

Metode POST

Jika metode POST membuat sumber daya baru, metode POST menampilkan kode status HTTP 201 (Dibuat). URI sumber daya baru disertakan dalam header Lokasi dari respons. Isi respons berisi representasi sumber daya.

Jika metode melakukan beberapa pemrosesan tetapi tidak membuat sumber daya baru, metode tersebut dapat menampilkan kode status HTTP 200 dan menyertakan hasil operasi di isi respons. Atau, jika tidak ada hasil untuk ditampilkan, metode ini dapat menampilkan kode status HTTP 204 (Tidak Ada Konten) tanpa isi respons.

Jika klien memasukkan data yang tidak valid ke dalam permintaan, server harus menampilkan kode status HTTP 400 (Permintaan Buruk). Isi respons dapat berisi informasi tambahan tentang kesalahan atau tautan ke URI yang memberikan detail selengkapnya.

Metode PUT

Jika metode PUT membuat sumber daya baru, metode PUT menampilkan kode status HTTP 201 (Dibuat), sama seperti metode POST. Jika metode memperbarui sumber daya yang ada, metode tersebut menampilkan 200 (OK) atau 204 (Tidak Ada Konten). Dalam beberapa kasus, tidak mungkin untuk memperbarui sumber daya yang ada. Dalam kasus ini, pertimbangkan untuk menampilkan kode status HTTP 409 (Konflik).

Pertimbangkan untuk menerapkan operasi HTTP PUT massal yang dapat melakukan pembaruan batch ke beberapa sumber daya dalam kumpulan. Permintaan PUT harus menentukan URI kumpulan, dan isi permintaan harus menentukan detail sumber daya yang akan diubah. Pendekatan ini dapat membantu mengurangi kerumitan dan meningkatkan performa.

Metode PATCH

Dengan permintaan PATCH, klien mengirimkan serangkaian pembaruan ke sumber daya yang ada, dalam bentuk dokumen patch. Server memproses dokumen patch untuk melakukan pembaruan. Dokumen patch tidak menjelaskan seluruh sumber daya, hanya serangkaian perubahan untuk diterapkan. Spesifikasi untuk metode PATCH (RFC 5789) tidak menentukan format tertentu untuk dokumen patch. Format harus disimpulkan dari jenis media dalam permintaan.

JSON mungkin adalah format data paling umum untuk API web. Ada dua format patch berbasis JSON utama, yang disebut patch JSON dan patch gabungan JSON.

Patch gabungan JSON lebih sederhana. Dokumen patch memiliki struktur yang sama dengan sumber daya JSON asli, tetapi hanya mencakup subset bidang yang harus diubah atau ditambahkan. Selain itu, bidang dapat dihapus dengan menetapkan null untuk nilai bidang dalam dokumen patch. (Ini berarti patch gabungan tidak cocok jika sumber daya asli dapat memiliki nilai null eksplisit.)

Misalnya, sumber daya asli memiliki representasi JSON berikut:

{
    "name":"gizmo",
    "category":"widgets",
    "color":"blue",
    "price":10
}

Berikut ini adalah kemungkinan patch gabungan JSON untuk sumber daya ini:

{
    "price":12,
    "color":null,
    "size":"small"
}

Hal ini memberitahu server untuk memperbarui price, menghapus color, dan menambahkan size, sementara name dan category tidak diubah. Untuk detail yang pasti dari patch gabungan JSON, lihat RFC 7396. Jenis media untuk patch gabungan JSON adalah application/merge-patch+json.

Patch gabungan tidak cocok jika sumber daya asli dapat berisi nilai null eksplisit, karena makna khusus null dalam dokumen patch. Selain itu, dokumen patch tidak menentukan urutan server harus menerapkan pembaruan. Hal ini mungkin atau mungkin tidak penting, bergantung pada data dan domain. Patch JSON, yang ditentukan dalam RFC 6902, lebih fleksibel. Patch JSON menetapkan perubahan sebagai urutan operasi untuk diterapkan. Operasi termasuk menambah, menghapus, mengganti, menyalin, dan menguji (untuk memvalidasi nilai). Jenis media untuk patch JSON adalah application/json-patch+json.

Berikut adalah beberapa kondisi kesalahan umum yang mungkin ditemui saat memproses permintaan PATCH, bersama dengan kode status HTTP yang sesuai.

Kondisi kesalahan Kode status HTTP
Format dokumen patch tidak didukung. 415 (Jenis Media Tidak Didukung)
Dokumen patch salah format. 400 (Permintaan Buruk)
Dokumen patch valid, tetapi perubahan tidak dapat diterapkan ke sumber daya dalam statusnya saat ini. 409 (Konflik)

Metode DELETE

Jika operasi penghapusan berhasil, server web harus merespons dengan kode status HTTP 204 (Tidak Ada Konten), yang menunjukkan bahwa proses telah berhasil ditangani, tetapi isi respons tidak berisi informasi selengkapnya. Jika sumber daya tidak ada, server web dapat menampilkan HTTP 404 (Tidak Ditemukan).

Operasi Asinkron

Terkadang operasi POST, PUT, PATCH, atau DELETE mungkin memerlukan pemrosesan yang memerlukan waktu beberapa saat untuk diselesaikan. Jika Anda menunggu penyelesaian sebelum mengirim respons ke klien, itu dapat menyebabkan latensi yang tidak dapat diterima. Jika demikian, pertimbangkan untuk membuat operasi yang asinkron. Tampilkan kode status HTTP 202 (Diterima) untuk menunjukkan bahwa permintaan telah diterima untuk diproses tetapi tidak diselesaikan.

Anda harus memperlihatkan titik akhir yang menampilkan status permintaan asinkron, sehingga klien dapat memantau status dengan polling titik akhir status. Sertakan URI titik akhir status di header Lokasi dari respons 202. Contohnya:

HTTP/1.1 202 Accepted
Location: /api/status/12345

Jika klien mengirimkan permintaan GET ke titik akhir ini, respons harus berisi status permintaan saat ini. Opsional, respons juga dapat mencakup perkiraan waktu penyelesaian atau tautan untuk membatalkan operasi.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "status":"In progress",
    "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }
}

Jika operasi asinkron membuat sumber daya baru, titik akhir status harus menampilkan kode status 303 (Lihat Lainnya) setelah operasi selesai. Dalam respons 303, sertakan header Lokasi yang memberikan URI sumber daya baru:

HTTP/1.1 303 See Other
Location: /api/orders/12345

Untuk informasi selengkapnya tentang cara menerapkan pendekatan ini, lihat Menyediakan dukungan asinkron untuk permintaan yang berjalan lama dan pola Balasan Permintaan Asinkron.

Set kosong dalam isi pesan

Setiap kali isi respons yang berhasil kosong, kode status harus 204 (Tidak Ada Konten). Untuk set kosong, seperti respons terhadap permintaan yang difilter tanpa item, kode status harus tetap 204 (Tidak Ada Konten), bukan 200 (OK).

Memfilter dan membuat halaman data

Mengekspos kumpulan sumber daya melalui satu URI dapat menyebabkan aplikasi mengambil data dalam jumlah besar ketika hanya sebagian informasi yang diperlukan. Misalnya, aplikasi klien perlu menemukan semua pesanan dengan biaya di atas nilai tertentu. Aplikasi klien mungkin mengambil semua pesanan dari URI /orders dan kemudian memfilter pesanan ini di sisi klien. Proses ini jelas sangat tidak efisien. Proses ini menyia-nyiakan bandwidth jaringan dan kekuatan pemrosesan pada server yang menghosting API web.

Sebaliknya, API dapat mengizinkan untuk meneruskan filter dalam string kueri URI, seperti /orders?minCost=n. API web kemudian bertanggung jawab untuk menguraikan dan menangani parameter minCost dalam string kueri dan menampilkan hasil yang difilter di sisi server.

Permintaan GET atas sumber daya kumpulan berpotensi mengembalikan sejumlah besar item. Anda harus mendesain API web untuk membatasi jumlah data yang dikembalikan oleh satu permintaan. Pertimbangkan untuk mendukung string kueri yang menentukan jumlah maksimum item yang akan diambil dan offset awal ke dalam kumpulan. Contohnya:

/orders?limit=25&offset=50

Selain itu, pertimbangkan untuk memberlakukan batasan atas jumlah item yang dikembalikan, untuk membantu mencegah Penolakan Serangan Layanan. Untuk membantu aplikasi klien, permintaan GET yang mengembalikan data yang dipaginasi juga harus menyertakan beberapa bentuk metadata yang menunjukkan jumlah total sumber daya yang tersedia dalam kumpulan.

Anda dapat menggunakan strategi yang sama untuk mengurutkan data saat diambil, dengan menyediakan parameter pengurutan yang menggunakan nama bidang sebagai nilainya, seperti /orders?sort=ProductID. Tetapi, pendekatan ini dapat memiliki efek negatif pada penembolokan, karena bentuk parameter string kueri merupakan bagian dari pengidentifikasi sumber daya yang digunakan oleh banyak penerapan cache sebagai kunci untuk data cache.

Anda dapat memperluas pendekatan ini untuk membatasi bidang yang dikembalikan untuk setiap item, jika setiap item berisi data dalam jumlah besar. Misalnya, Anda dapat menggunakan parameter string kueri yang menerima daftar bidang yang dipisahkan koma, seperti /orders?fields=ProductID,Quantity.

Berikan semua parameter opsional dalam string kueri sebagai default. Misalnya, atur parameter limit ke 10 dan parameter offset ke 0 jika Anda menerapkan penomoran halaman, atur parameter urutan ke kunci sumber daya jika Anda menerapkan pengurutan, dan atur parameter fields ke semua bidang di sumber daya jika Anda mendukung proyeksi.

Mendukung respons parsial untuk sumber daya biner besar

Sumber daya mungkin berisi bidang biner besar, seperti file atau gambar. Untuk mengatasi masalah yang disebabkan oleh koneksi yang tidak dapat diandalkan serta terputus-putus dan untuk meningkatkan waktu respons, pertimbangkan untuk mengaktifkan sumber daya tersebut untuk diambil dalam gugus. Untuk melakukan hal ini, API web harus mendukung header Accept-Ranges untuk permintaan GET untuk sumber daya yang besar. Header ini menunjukkan bahwa operasi GET mendukung permintaan parsial. Aplikasi klien dapat mengirimkan permintaan GET yang menghasilkan subset sumber daya, yang ditentukan sebagai rentang byte.

Selain itu, pertimbangkan untuk menerapkan permintaan HTTP HEAD untuk sumber daya ini. Permintaan HEAD mirip dengan permintaan GET, kecuali hanya menampilkan header HTTP yang menjelaskan sumber daya, dengan isi pesan kosong. Aplikasi klien dapat mengeluarkan permintaan HEAD untuk menentukan apakah akan mengambil sumber daya dengan menggunakan permintaan GET parsial. Contohnya:

HEAD https://adventure-works.com/products/10?fields=productImage HTTP/1.1

Berikut adalah contoh pesan respons:

HTTP/1.1 200 OK

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 4580

Header Content-Length memberikan ukuran total sumber daya, dan header Accept-Ranges menunjukkan bahwa operasi GET yang sesuai mendukung hasil parsial. Aplikasi klien dapat menggunakan informasi ini untuk mengambil gambar dalam gugus yang lebih kecil. Permintaan pertama mengambil 2500 byte pertama dengan menggunakan header Range:

GET https://adventure-works.com/products/10?fields=productImage HTTP/1.1
Range: bytes=0-2499

Pesan respons menunjukkan bahwa ini adalah respons parsial dengan menampilkan kode status HTTP 206. Header Content-Length menentukan jumlah sebenarnya dari byte yang ditampilkan dalam isi pesan (bukan ukuran sumber daya), dan header Content-Range menunjukkan byte termasuk pada sumber daya ini (byte 0-2499 dari 4580):

HTTP/1.1 206 Partial Content

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580

[...]

Permintaan berikutnya dari aplikasi klien dapat mengambil sisa sumber daya.

Salah satu motivasi utama di balik REST adalah bahwa memungkinkan menavigasi seluruh rangkaian sumber daya tanpa memerlukan pengetahuan sebelumnya tentang skema URI. Setiap permintaan HTTP GET harus menampilkan informasi yang diperlukan untuk menemukan sumber daya yang terkait langsung dengan objek yang diminta melalui hyperlink yang disertakan dalam respons, dan juga harus diberikan informasi yang menjelaskan operasi yang tersedia pada masing-masing sumber daya ini. Prinsip ini dikenal sebagai HATEOAS, atau Hypertext as the Engine of Application State. Sistem merupakan mesin status terbatas, dan respons terhadap setiap permintaan berisi informasi yang diperlukan untuk berpindah dari satu status ke status lain; tidak ada informasi lain yang diperlukan.

Catatan

Saat ini tidak ada standar tujuan umum yang menentukan cara memodelkan prinsip HATEOAS. Contoh-contoh yang ditunjukkan di bagian ini menggambarkan satu kemungkinan, solusi hak milik.

Misalnya, untuk menangani hubungan antara pesanan dan pelanggan, representasi pesanan dapat mencakup tautan yang mengidentifikasi operasi yang tersedia untuk pelanggan pesanan. Berikut adalah representasi yang memungkinkan:

{
  "orderID":3,
  "productID":2,
  "quantity":4,
  "orderValue":16.60,
  "links":[
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"DELETE",
      "types":[]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"DELETE",
      "types":[]
    }]
}

Dalam contoh ini, array links memiliki sekumpulan tautan. Setiap tautan mewakili operasi pada entitas terkait. Data untuk setiap tautan mencakup hubungan ("pelanggan"), URI (https://adventure-works.com/customers/3), metode HTTP, dan jenis MIME yang didukung. Ini semua adalah informasi yang dibutuhkan aplikasi klien agar dapat memanggil operasi.

Array links juga menyertakan informasi referensi mandiri tentang sumber daya itu sendiri yang telah diambil. Array memiliki hubungan self.

Kumpulan tautan yang ditampilkan dapat berubah, bergantung pada status sumber daya. Ini adalah yang dimaksud dengan hypertext sebagai "mesin status aplikasi."

Menerapkan versi API web RESTful

Sangat tidak mungkin bahwa API web akan tetap statik. Saat persyaratan bisnis berubah, kumpulan sumber daya baru dapat ditambahkan, hubungan antara sumber daya mungkin berubah, dan struktur data dalam sumber daya dapat diubah. Meskipun memperbarui API web untuk menangani persyaratan baru atau yang berbeda adalah proses yang relatif mudah, Anda harus mempertimbangkan efek perubahan tersebut pada aplikasi klien yang menggunakan API web. Masalahnya adalah meskipun pengembang yang merancang dan menerapkan API web memiliki kontrol penuh atas API tersebut, pengembang tidak memiliki tingkat kontrol yang sama atas aplikasi klien, yang mungkin dibangun oleh organisasi pihak ketiga yang beroperasi dari jarak jauh. Keharusan utama adalah untuk memungkinkan aplikasi klien yang ada agar terus berfungsi dan tidak berubah sekaligus memungkinkan aplikasi klien baru mengambil keuntungan dari fitur dan sumber daya baru.

Penerapan versi memungkinkan API web untuk menunjukkan fitur dan sumber daya yang ditampilkan, dan aplikasi klien dapat mengirimkan permintaan yang diarahkan ke versi fitur atau sumber daya tertentu. Bagian berikut menjelaskan beberapa pendekatan yang berbeda, masing-masing memiliki keuntungan dan kekurangan sendiri.

Tidak ada penerapan versi

Ini adalah pendekatan paling sederhana, dan mungkin dapat diterima untuk beberapa API internal. Perubahan signifikan dapat direpresentasikan sebagai sumber daya baru atau tautan baru. Menambahkan konten ke sumber daya yang ada mungkin tidak memberikan perubahan yang merusak karena aplikasi klien yang tidak mengharapkan untuk melihat konten ini akan mengabaikannya.

Misalnya, permintaan ke https://adventure-works.com/customers/3 URI harus menampilkan detail satu pelanggan yang berisi bidang id, name, dan address yang diharapkan oleh aplikasi klien:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Catatan

Untuk mempermudah, contoh respons yang ditampilkan di bagian ini tidak menyertakan tautan HATEOAS.

Jika bidang DateCreated ditambahkan ke skema sumber daya pelanggan, maka responsnya akan terlihat seperti ini:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}

Aplikasi klien yang ada mungkin terus berfungsi dengan benar jika aplikasi klien mampu mengabaikan bidang yang tidak dikenal, sementara aplikasi klien baru dapat dirancang untuk menangani bidang baru ini. Tetapi, jika terjadi perubahan yang lebih radikal pada skema sumber daya (seperti menghapus atau mengganti nama bidang) atau hubungan antara sumber daya berubah, maka ini mungkin merupakan perubahan yang merusak yang mencegah aplikasi klien yang ada berfungsi dengan benar. Dalam situasi ini, Anda harus mempertimbangkan salah satu pendekatan berikut.

Penerapan versi URI

Setiap kali mengubah API web atau mengubah skema sumber daya, Anda menambahkan nomor versi ke URI untuk setiap sumber daya. URI yang ada sebelumnya harus terus beroperasi seperti sebelumnya, mengembalikan sumber daya yang sesuai dengan skema aslinya.

Memperluas contoh sebelumnya, jika bidang address direstrukturisasi menjadi sub bidang yang berisi setiap bagian penyusun alamat (seperti streetAddress, city, state, dan zipCode), versi sumber daya ini dapat ditampilkan melalui URI yang berisi nomor versi, seperti https://adventure-works.com/v2/customers/3:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Mekanisme penerapan versi ini sangat sederhana tetapi bergantung pada server yang merutekan permintaan ke titik akhir yang sesuai. Tetapi, mekanisme ini dapat menjadi susah digunakan karena API web akan menjadi siap melalui beberapa perulangan dan server harus mendukung sejumlah versi yang berbeda. Selain itu, dari sudut pandang murni, dalam semua kasus, aplikasi klien mengambil data yang sama (pelanggan 3), jadi URI seharusnya tidak terlalu berbeda bergantung pada versinya. Skema ini juga memperumit penerapan HATEOAS karena semua tautan harus menyertakan nomor versi di URI.

Penerapan versi string kueri

Daripada menyediakan beberapa URI, Anda dapat menentukan versi sumber daya dengan menggunakan parameter dalam string kueri yang ditambahkan ke permintaan HTTP, seperti https://adventure-works.com/customers/3?version=2. Parameter versi harus default ke nilai yang berarti seperti 1 jika dihilangkan oleh aplikasi klien yang lebih lama.

Pendekatan ini memiliki keuntungan semantik bahwa sumber daya yang sama selalu diambil dari URI yang sama, tetapi hal ini bergantung pada kode yang menangani permintaan untuk mengurai string kueri dan mengirim kembali respons HTTP yang sesuai. Pendekatan ini juga mengalami komplikasi yang sama untuk menerapkan HATEOAS sebagai mekanisme penerapan versi URI.

Catatan

Beberapa browser web dan proksi web lama tidak akan menyimpan respons untuk permintaan yang menyertakan string kueri di URI. Hal ini dapat menurunkan performa untuk aplikasi web yang menggunakan API web dan yang dijalankan dari dalam browser web tersebut.

Penerapan versi header

Daripada menambahkan nomor versi sebagai parameter string kueri, Anda dapat menerapkan header kustom yang menunjukkan versi sumber daya. Pendekatan ini mengharuskan aplikasi klien menambahkan header yang sesuai ke permintaan apa pun, meskipun kode yang menangani permintaan klien dapat menggunakan nilai default (versi 1) jika header versi dihilangkan. Contoh berikut menggunakan header kustom bernama Custom-Header. Nilai dari header ini menunjukkan versi API web.

Versi 1:

GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Versi 2:

GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=2
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Seperti dua pendekatan sebelumnya, penerapan HATEOAS memerlukan penyertaan header kustom yang sesuai di tautan mana pun.

Penerapan versi jenis media

Saat aplikasi klien mengirimkan permintaan HTTP GET ke server web, aplikasi tersebut harus menetapkan format konten yang dapat ditanganinya dengan menggunakan header Terima, seperti yang dijelaskan sebelumnya dalam panduan ini. Sering kali tujuan dari header Terima adalah untuk mengizinkan aplikasi klien menentukan apakah isi respons harus berupa XML, JSON, atau format umum lainnya yang dapat diurai oleh klien. Tetapi, dimungkinkan untuk menentukan jenis media kustom yang menyertakan informasi yang memungkinkan aplikasi klien menunjukkan versi sumber daya yang diharapkan.

Contoh berikut menunjukkan permintaan yang menetapkan header Terima dengan nilai application/vnd.adventure-works.v1+json. Elemen vnd.adventure-works.v1 menunjukkan ke server web bahwa elemen harus mengembalikan sumber daya versi 1, sedangkan elemen json menetapkan bahwa format isi respons harus JSON:

GET https://adventure-works.com/customers/3 HTTP/1.1
Accept: application/vnd.adventure-works.v1+json

Kode yang menangani permintaan bertanggung jawab untuk memproses header Terima dan menerapkannya selama mungkin (aplikasi klien dapat menentukan beberapa format dalam header Terima, dalam kasus ini server web dapat memilih format yang paling sesuai untuk isi respons). Server web mengonfirmasi format data di isi respons dengan menggunakan header Content-Type:

HTTP/1.1 200 OK
Content-Type: application/vnd.adventure-works.v1+json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Jika header Terima tidak menentukan jenis media yang diketahui, server web dapat menghasilkan pesan respons HTTP 406 (Tidak Dapat Diterima) atau menampilkan pesan dengan jenis media default.

Pendekatan ini bisa dibilang yang paling murni dari mekanisme penerapan versi dan cocok untuk HATEOAS, yang dapat menyertakan jenis MIME data terkait dalam tautan sumber daya.

Catatan

Saat memilih strategi penerapan versi, Anda juga harus mempertimbangkan implikasinya pada performa, terutama penembolokan di server web. Skema penerapan versi URI dan penerapan versi String Kueri mudah disimpan karena kombinasi string URI/kueri yang sama merujuk pada data yang sama setiap waktu.

Mekanisme penerapan versi Header dan Jenis Media biasanya memerlukan logika tambahan untuk memeriksa nilai di header kustom atau header Terima. Dalam lingkungan skala besar, banyak klien yang menggunakan versi berbeda dari API web dapat menghasilkan sejumlah besar data duplikat di cache sisi server. Masalah ini dapat menjadi akut jika aplikasi klien berkomunikasi dengan server web melalui proksi yang menerapkan penembolokan, dan hanya meneruskan permintaan ke server web jika saat ini tidak menyimpan salinan data yang diminta di dalam cache.

Open API Initiative

Open API Initiative dibuat oleh konsorsium industri untuk menstandardisasi deskripsi REST API di seluruh vendor. Sebagai bagian dari inisiatif ini, spesifikasi Swagger 2.0 diubah namanya menjadi OpenAPI Specification (OAS) dan dibawa ke bawah Open API Initiative.

Anda mungkin ingin mengadopsi OpenAPI untuk API web Anda. Poin yang perlu dipertimbangkan:

  • Spesifikasi OpenAPI hadir dengan seperangkat panduan berpendirian tentang cara REST API harus dirancang. OpenAPI memiliki keuntungan untuk interoperabilitas, tetapi membutuhkan lebih banyak perhatian saat merancang API Anda agar sesuai dengan spesifikasi.

  • OpenAPI mempromosikan pendekatan contract-first, bukan pendekatan implementation-first. Contract-first berarti Anda mendesain kontrak API (antarmuka) terlebih dahulu dan kemudian menulis kode yang menerapkan kontrak tersebut.

  • Alat seperti Swagger dapat menghasilkan pustaka klien atau dokumentasi dari kontrak API. Misalnya, lihat Halaman bantuan ASP.NET Web API menggunakan Swagger.

Langkah berikutnya

  • Panduan Microsoft Azure REST API. Rekomendasi terperinci untuk merancang REST API di Azure.

  • Daftar periksa API Web. Daftar item yang berguna untuk dipertimbangkan saat merancang dan menerapkan API web.

  • Open API Initiative. Detail dokumentasi dan penerapan pada Open API.