Perubahan API

Deprecation GraphQL tanpa nomor versi

5 menit baca

API REST bisa merilis /v2/ berdampingan dengan /v1/ dan membiarkan pemanggil berpindah sesuai kecepatan mereka sendiri. GraphQL punya satu skema di satu endpoint, dan setiap client, aplikasi mobile dengan build tahun lalu dan dashboard internal yang dirilis pagi ini, mengkueri graph yang sama. Tidak ada URL untuk di-fork. Men-deprecate sebuah field berarti menandainya sebagai deprecated di tempat, dalam skema yang sudah menjadi tumpuan semua orang, yang membuat disiplinnya berbeda dari REST meskipun masalah dasarnya, memberi tahu pemanggil bahwa sesuatu akan hilang, sama dengan yang dibahas deprecation API secara umum.

Bagaimana GraphQL menandai field sebagai deprecated, jika tidak ada versi untuk dinaikkan?

Dengan directive @deprecated, diterapkan langsung pada field:

type Product {
  price: Float @deprecated(reason: "Use priceV2 for multi-currency support.")
  priceV2: Money
}

Field tetap bisa dikueri. Ia tidak hilang, tidak mengembalikan 404, tidak mengubah perilaku; ia hanya membawa catatan yang bisa dibaca mesin yang akan ditampilkan sebagian besar alat GraphQL, GraphiQL, Apollo Studio, linter skema, kepada siapa pun yang menelusuri skema atau menulis kueri terhadapnya. Ini seluruh mekanismenya. Tidak ada endpoint deprecation terpisah, tidak ada header, tidak ada dokumen pendamping yang diwajibkan spec, yang menjadi baik daya tariknya maupun jebakannya: directive mudah ditambahkan dan mudah diabaikan, karena tidak ada yang memaksa client untuk melihatnya.

Apakah ada yang benar-benar melihat alasan deprecation?

Hanya yang menggunakan skema secara langsung, lewat introspection atau editor yang sadar skema, dan itu audiens yang lebih kecil daripada pembaca biasa changelog API. Aplikasi mobile yang dibangun terhadap sebuah kueri enam bulan lalu sudah memanggang kueri itu ke dalam binary-nya; ia akan terus meminta price dan terus mendapat jawaban, deprecated atau tidak, sampai seseorang membangun ulang aplikasi dengan field baru dan merilis pembaruan. Directive memberi tahu developer yang menulis kode baru untuk tidak menggunakan field lama. Ia tidak melakukan apa pun untuk client yang sudah dirilis dan berjalan.

MekanismeSiapa yang terjangkau
Directive @deprecatedDeveloper yang menelusuri skema atau menulis kueri baru
Kegagalan CI dari linter skemaTim pemilik basis kode client, jika mereka menjalankan satu
Entri changelogSiapa pun yang membacanya, termasuk tim client tanpa linter
Tidak ada (field tetap berfungsi)Client yang sudah dibangun yang menggunakan field lama

Apakah field yang deprecated tetap harus mendapat entri changelog?

Ya, dan itu melakukan lebih banyak pekerjaan daripada directive saja, karena changelog menjangkau orang yang tidak bisa dijangkau directive: tim mitra yang mengonsumsi graph tanpa menelusuri skemanya, client yang dibangun terhadap salinan skema yang di-cache berbulan-bulan lalu, siapa pun yang hanya akan menyadarinya dengan membaca prosa. Changelog API membahas secara umum apa yang menjadi kewajiban entri kepada pemanggil; entri GraphQL berutang satu hal yang jarang harus dijelaskan REST, karena pemanggil REST menyimpulkannya dari nomor versi: apakah field lama masih berfungsi hari ini, masih berfungsi dengan peringatan, atau benar-benar berhenti mengembalikan data. Directive saja tidak menjawab semua itu untuk pembaca yang tidak pernah membuka skema.

Kapan sebenarnya aman menghapus field dari skema?

Hanya setelah log kueri menunjukkan tidak ada yang memintanya lagi, yang merupakan pertanyaan penggunaan, bukan pertanyaan kalender. Sebuah field bisa membawa @deprecated selama setahun dan tetap menjadi penopang bagi satu client yang tidak pernah dibangun ulang; menghapusnya pada jadwal tetap, seperti yang sering dilakukan Sunset REST, merusak client itu tanpa peringatan yang bisa ditindaklanjuti, karena GraphQL tidak memberinya apa pun untuk ditindaklanjuti selain directive yang tidak pernah dibacanya. Catat penggunaan tingkat field sebelum berkomitmen pada tanggal penghapusan, dan perlakukan hitungan kueri bukan nol sebagai jeda, bukan hitung mundur.

Apakah menambah field membawa risiko yang sama seperti di API REST?

Lebih rendah, untuk field baru, karena client GraphQL hanya menerima field yang diminta secara eksplisit. Menambah priceV2 di samping price tidak bisa merusak kueri yang ada dengan cara menambah field ke respons JSON REST bisa merusak deserializer yang ketat, karena tidak ada yang memaksa client meminta field baru. Menambah nilai ke enum yang sudah ada adalah pengecualian yang layak disebutkan dalam napas yang sama: client yang men-switch pada setiap nilai enum secara ekshaustif, sesuatu yang didorong bahasa dengan tipe kuat, rusak begitu nilai baru muncul, terlepas apakah ada kueri yang memintanya atau tidak. Keamanan ini hanya berlaku untuk field dan anggota union yang dipilih sendiri oleh client; tidak berlaku untuk himpunan tertutup yang dienumerasi tangan oleh kode client.

Apa yang dibutuhkan entri changelog GraphQL yang tidak dibutuhkan entri REST?

Bentuk kueri, bukan hanya nama field, karena “field price deprecated” kehilangan bagian yang benar-benar dibutuhkan pemanggil: tipe mana dan kueri mana yang menyentuhnya. Entri yang berguna menyebutkan tipe, field, field pengganti, dan, jika bisa dibuat, kueri aktual di produksi yang masih meminta bentuk lama. Bagian terakhir itu, mengaitkan pemberitahuan deprecation dengan penggunaan nyata, adalah yang didapat gratis oleh pemanggil REST dari log server pada sebuah URL dan tidak didapat pemanggil GraphQL, karena setiap kueri mengenai endpoint yang sama apa pun yang dimintanya.

Adakah selain field yang bisa membawa directive @deprecated?

Nilai enum, menggunakan directive yang sama di definisi nilai itu sendiri, bukan di field:

enum ShippingMethod {
  STANDARD
  EXPRESS
  OVERNIGHT @deprecated(reason: "Use EXPRESS with priority: true instead.")
}

Spec mendefinisikan @deprecated untuk tepat dua lokasi, definisi field atau nilai enum, dan tidak ada yang lain per rilis stabilnya; deprecation tingkat argumen dan input-field hanya ada dalam bahasa draft yang lebih baru, bukan dalam apa yang diimplementasikan kebanyakan server hari ini. Nilai enum yang ditandai dengan cara ini tetap menjadi nilai sah yang masih bisa dikembalikan atau diterima server, janji tidak-breaking yang sama seperti yang dibuat field yang deprecated, yang membuatnya aman dirilis sebelum benar-benar menghapus nilai itu.

FAQ

Apakah GraphQL mendukung sesuatu seperti header Sunset untuk seluruh endpoint? Tidak, karena biasanya hanya ada satu endpoint. Waktu deprecation hidup di tingkat field, dalam teks alasan directive @deprecated dan dalam changelog atau panduan migrasi apa pun yang diterbitkan tim di sampingnya, bukan dalam header respons yang bisa dibaca client secara programatik.

Bisakah field yang deprecated dihapus lalu ditambahkan kembali nanti dengan tipe berbeda? Hanya sebagai nama field baru. Memperkenalkan kembali nama field yang sama dengan tipe yang berubah adalah persis breaking change yang ingin dihindari siklus deprecation; beri pengganti namanya sendiri, seperti yang dilakukan priceV2, dan biarkan yang lama benar-benar punah sebelum namanya bebas untuk digunakan lagi.

Haruskah teks alasan @deprecated menautkan ke entri changelog? Ya, ketika tooling skema mendukungnya. Field alasan menerima string biasa, dan URL di dalam string itu adalah jalan terpendek dari developer yang menatap output introspection ke penjelasan lebih lengkap yang bisa diberikan entri changelog.

Apakah perubahan skema GraphQL pernah backward compatible dengan cara yang tidak dimiliki REST? Perubahan field aditif, ya, karena alasan di atas: client hanya mendapat apa yang mereka minta. Nilai enum baru adalah pengecualiannya, karena client yang mengenumerasi himpunan tertutup bisa rusak pada nilai yang tidak diperkirakannya. Penghapusan dan perubahan tipe persis sama breaking-nya dengan padanan RESTnya.


Klaim teknis dalam artikel ini belum ditinjau secara independen. Jika ada yang keliru, beri tahu kami dan kami akan memperbaikinya.

Terkait di changeloop: Dokumentasi developer, Perbandingan alat changelog

changeloop
Tim di balik changelog yang menutup lingkaran. Pengguna meminta sesuatu, timmu mengirimkannya, yang meminta jadi tahu.