Blog

🤔 GraphQL Farklı Kullanıcılar için Farklı Olmalı mı?

Leonardo Losoviz
Yazan: Leonardo Losoviz ·

GraphQL, bir kaynaktan veri almak için kullanılan bir arayüzdür; GraphQL spec ise bu arayüzün gereksinimlerini tanımlar. Bu gereksinimler karşılandığı sürece, bunun nasıl gerçekleştirildiği GraphQL açısından önemli değildir. GraphQL sunucusu JavaScript'te promise'lar kullanılarak, Golang tabanlı eşzamanlı bir mimariyle, bir Excel dosyasına eşlenerek ya da başka yollarla uygulanabilir ve bunların hepsi GraphQL spec'in geçerli uygulamaları olabilir.

GraphQL, istemci ile backend servisleri arasında yer alır

Sunucunun motorunun nasıl uygulandığı, bir GraphQL isteğinin başarıyla yürütülmesi açısından önemli değildir; çünkü istemci ile sunucu arasındaki etkileşim her zaman aynıdır: tanımlanmış bir sözdizimi kullanılarak bir GraphQL query gönderilir ve JSON formatında karşılık gelen bir yanıt alınır.

Şimdi, uygulamanın önemli olmadığını söylediğimde, bunu API kullanıcısının bakış açısından kastediyorum; bu kullanıcı yalnızca sunucudan veri almayı amaçlamaktadır. Döndürülen verilerin nasıl üretildiği onun için bir önem taşımaz.

Ancak durum, API üzerinde çalışan sunucu tarafı geliştirici için değişmektedir; bu geliştirici için uygulama ayrıntıları gerçekten çok önemlidir. GraphQL API'mi PHP'de kodlarsam, API'min mümkün olduğunca verimli çözülmesi ve PHP'nin sunduğu yetenekleri kullanarak mümkün olduğunca zarif bir mimari tasarıma sahip olması için elimden geleni yaparım.

PHP vs Java vs JavaScript

Bu noktada, API'yi koruma ihtiyacı ile API üzerinde çalışan geliştiricilerin beklediği yetenekler arasında olası bir çıkar çatışması ortaya çıkmaktadır; geliştiriciler, temel dil tarafından desteklenen özelliklerin (özyinelemeli kod çalıştırabilme gibi) ellerinden alınmasını istemez.

Bu çatışma, #929: Allow recursive references in fragments issue'sunda açıkça ortaya çıkmıştır; bu issue, GraphQL'in fragment'larda özyinelemeli referanslara izin vermemesinin yanlış olduğunu savunmaktadır.

GraphQL çalışma grubunun geçmişteki bir meetup'ında, issue'yu açan geliştirici olan Roman, spec'in getirdiği kısıtlamaya neden katılmadığını şu şekilde ifade etti:

Ben bir sunucu tarafı geliştiriciyim ve spec'in sunucu tarafı yürütme hakkında fazla konuştuğunu düşünüyorum; oysa istemcinin ne almak istediğine odaklanmalı — nasıl alınacağına değil

Fragment'larda özyinelemeyi yasaklayan kural, genel API'yi güvende tutma gerekçesiyle haklı kılınmıştır. Sonuçta GraphQL, Facebook tarafından kamuya açık uygulamasına veri sunmak amacıyla oluşturulmuştur ve kullanıcıların hizmeti çökertebilecek bir API tasarım açığını istismar edememeleri gerekir.

GraphQL'in yaratıcısı Lee Byron üç temel endişesini dile getirdi:

sonsuz özyineleme; kısıtlamalar yalnızca bir spec olmayacak — nasıl ve ne zaman durmalı

veri doğrulama; aynı değerin birden fazla kez döndürülmesi, bu veride nasıl temsil edilir. İdeal olarak döngüsel olduğunu tespit edip hemen durmasını istersiniz, ancak bazı sunucular bunu tespit edemez ve bir şeylerin yanlış gittiğini anlayıp durmadan önce döngüde birçok kez dönebilir

bunu olmamanın maliyeti nedir; bu sorunları haklı kılıyor mu? Hayır, kılmıyor; query'nizde kaç seviye derinliğe ineceğinizi her zaman belirtmeniz mümkündür — bu, bunu GraphQL'de ele alsaydık yapacağımız şeyin özünde "desugared" versiyonudur

Kendi bakış açılarından hareketle hem Roman hem de Lee haklıdır. Lee Byron, kamuya açık GraphQL API'nin güvenliği konusunda endişelidir. Özyinelemeli fragment'lardan kaçınmak, kötü niyetli kişilerin query'de hiç bitmeyecek bir döngüsel döngü çalıştırarak sistemi çökertememesini sağlamak ve hatta bir ekibin farkında olmadan sistemi durduran bir query yayınlamasından kaynaklanan "self-DDoSing" riskini ortadan kaldırmak için haklı görünmektedir.

Roman ise kendi GraphQL API'sini oluşturma kapasitesine getirilen kısıtlamalar konusunda endişelidir. Roman API'sinin tek tüketicisi olabileceğinden (yani kullanıcılara açık olmayan özel bir API), ya da sunucusu yinelenen döngüleri tespit edip durdurma kapasitesine sahip olabileceğinden, GraphQL kısıtlamasının zararlı ve haklı gerekçeden yoksun olduğunu düşünmektedir.

Tartışmanın özünde mesele, özyinelemeli fragment'lara izin verilip verilmemesi değil, daha temel bir şeydir: GraphQL'in hedefi kim? Tek bir grup değilse, tek bir API spec'i tüm farklı paydaşların gereksinimlerini karşılayabilir mi? Çatışma önlenemiyorsa en azından bir şekilde giderilebilir mi?

Bu soruları inceleyelim.

GraphQL'in Hedefi Kim?

GraphQL, aralarında şunları tanımlayabileceğimiz farklı paydaş türleri tarafından kullanılmaktadır:

1. API kullanıcıları: Herhangi bir nedenle bir GraphQL endpoint'inden veri tüketenler. Örneğin, GitHub repolarımızla ilgili verileri almak için GitHub'ın genel GraphQL API'sini kullanan API kullanıcıları olabiliriz.

2. İstemci tarafı geliştiriciler: Bir GraphQL endpoint'i tarafından desteklenen istemci tarafı uygulamaları oluşturanlar. Örneğin, Gatsby ile site oluşturan geliştiriciler, site içeriğini almak için GraphQL'e güvenir.

3. Backend geliştiriciler: GraphQL API için resolver'lar oluşturanlar.

Ayrıca, GraphQL API'nin genel veya özel olabileceğini belirtmeliyiz:

Genel API: GraphQL endpoint'ine herkesin erişebildiği için, kötü niyetli kişilerin saldırılarını önlemek amacıyla güvenlik önlemlerine dikkat etmek zorundayız.

Özel API: Yalnızca yetkili kişilerin API'ye erişim izni aldığı için, doğası gereği güvenlik riski yoktur ve self-DDoSing, iyi kodlama uygulamalarıyla kolayca önlenebilir.

Tek Bir API Spec'i Tüm Paydaşların Gereksinimlerini Karşılar mı?

Roman'ın açtığı issue şöyle yorumlanabilir: "Eğer GraphQL API'm özelse ve ne yaptığımı kesinlikle biliyorsam (kodumun beklendiği gibi çalışacağına ve hiçbir durma yürütmesinin üretilmeyeceğine %100 emin olmak), o zaman neden fragment'larda özyineleme kullanamayayım?"

Özyinelemeler @ xkcd

Bu duruma bir örnek, statik siteler oluşturmak için GraphQL destekli bir çerçeve kullandığımızda (Gatsby, Next.js veya RedwoodJS gibi) yaşanır; çünkü GraphQL API genellikle özel olacaktır ve uygulamayı DDoS'lamak ve olumsuz sonuçlara katlanmak için farkında olmadan bir şey yapma ihtimalimiz yoktur (en kötü ihtimalle geliştirme veya staging ortamında statik siteyi oluştururken çöker).

Yukarıdaki kurulumu kullanan geliştiriciler, GraphQL spec'in kendi kurulumları için hiçbir olumsuz sonucu olmayan yararlı özellikleri kullanmayı neden yasakladığını pekâlâ merak edebilir.

Sonuç olarak, özyinelemeli fragment'ları yasaklayarak GraphQL spec, tüm potansiyel GraphQL kullanımlarının bir seçimine uygulanan — hepsine değil — bir güvenlik önlemi dayatmaktadır; bunu güvende olmak adına yapmaktadır.

GraphQL Spec Tüm Paydaşları Daha İyi Tatmin Edebilir mi?

Farklı paydaşların farklı gereksinimleri varsa, GraphQL spec hepsini nasıl tatmin edebilir? (Fikir, spec'i çatallamaktan ve belirli hedefler için özelleştirilmiş versiyonlar üretmekten kaçınmaktır.)

Birincisi spec-katkı sürecinden geçmesi gereken, ikincisi ise gerektirmeyen birkaç fikri inceleyelim.

GraphQL Spec Düzeyinde Feature-Toggle

Alınabilecek olası bir yol, spec'in kuralları "önermesi" ama "dayatmaması"dır. Bu durumda, fragment'larda özyinelemeyi yasaklayan kural güçlü bir şekilde önerilebilir, ancak özellik yine de kabul edilebilir olur.

Şimdi, bu çözüm özyinelemeli fragment'ların varsayılan koşulunu "zorunlu"dan "isteğe bağlı"ya değiştirirdi; bu da iki olumsuz sonuç doğururdu:

  • API varsayılan olarak güvensiz olurdu (Lee Byron'ın önlemek istediği senaryo)
  • Yasaklanmış bir query'ye daha sonra izin verileceğinden, bu bir breaking change üretirdi

O zaman seçeneği tersyüz etmek daha iyi olur: fragment'larda özyinelemenin varsayılan olarak hâlâ yasaklı kalmasını sağlarken, bu davranışı devre dışı bırakan bir feature-flag'i etkinleştirme imkânı sunulur. Özellik açıkça devre dışı bırakılması gerektiğinden, bunu yalnızca ne yaptıklarını bilen yöneticiler yapacaktır.

Özellik belirli kurulumlar altında en değerli olduğundan, GraphQL sunucuları ve çerçeveleri yapılandırmayı ne zaman/nasıl/ne kadar sunacaklarına karar verebilir. Örneğin Gatsby, statik siteler oluştururken seçeneği bir arayüz aracılığıyla belirgin biçimde görüntüleyebilir ve aksi takdirde gizleyebilir.

Genel fikir, GraphQL spec'in yapılandırma yoluyla etkinleştirilebilir/devre dışı bırakılabilecek ve varsayılan durumu spec'te zaten sahip oldukları durum olan "etkin ancak isteğe bağlı özellikler"i desteklemesidir.

Özyinelemeli fragment'ların yasaklanması bunlardan biri olacaktır ve bir Map türü gibi başka özellikler de olabilir; bu, Lee Byron tarafından şu gerekçeyle spec'e kabul edilmedi:

Map türü ile anahtar/değer çiftleri listesi arasında önemli takaslar vardır. Bir sorun, koleksiyon üzerinde sayfalama yapmaktır. Değer listeleri için açık sayfalama kuralları olabilirken, genellikle sırasız anahtar-değer çiftlerine sahip Map'ler sayfalama açısından çok daha zordur.

Diğer bir sorun ise kullanımdır. Çoğu zaman Map, değerin bir alanının dizinlendiği API'lerde kullanılır; bu bence bir API anti-desenidir, çünkü dizinleme bir depolama sorunu ve bir istemci önbellekleme sorunudur ama bir taşıma sorunu değildir. Bu anti-desen beni endişelendiriyor. API'lerde Map için bazı iyi kullanım senaryoları olsa da, yaygın kullanımın bu anti-desenler için olacağından korkuyorum, bu nedenle dikkatli ilerlemeyi öneriyorum.

Lee Byron, özelliğin bir anti-desen olarak kullanılacağı korkusunu dile getirdi. Ancak bunun için iyi kullanım senaryoları olduğunu da kabul etti. O hâlde, issue topluluktan büyük destek topladığından (150'den fazla 👍 ile), geliştiricilere şemalarına Map türü eklenmesini açıkça etkinleştirme ve sonuçlarıyla başa çıkma seçeneği verilebilir.

GraphQL Sunucuları Tarafından Feature-Toggle

Yukarıdaki öneri, GraphQL spec için çok riskli olduğu gerekçesiyle destek toplamıyorsa, bir alternatif bunu GraphQL sunucu düzeyinde uygulamaktır. Bu durumda GraphQL sunucuları, fragment'larda özyinelemeyi devre dışı bırakan özel bir özellik sunabilir.

Fikri genelleştirirsek, GraphQL sunucuları spec'ten bazı özellikleri devre dışı bırakmayı ve spec'te eksik olan diğerlerini etkinleştirmeyi sunabilir. Bu davranışın sürpriz yaratmaması için, sunucular varsayılan durumun spec'in gerektirdiği durum olduğundan emin olmalı ve API yöneticisi, özelliği etkinleştirmenin sonuçları konusunda tamamen bilinçli kılınmalıdır. (Bu, Gato GraphQL'in "innovative features" için izlediği stratejidir.)

Sonuç

GraphQL giderek daha popüler hâle geldikçe, yeni yetenekleri destekleyen yeni çerçeveler onu stack'lerine dahil etmiş ve yeni paydaşlar (ve bunların yeni türleri) devreye girmiştir. Bu nedenle, başlangıçta Facebook tarafından uygulamalarının sunucularından nasıl veri alacağını tanımlamak amacıyla oluşturulan bir spec, giderek daha fazla kullanım senaryosuyla yüzleşmek zorunda kalmaktadır.

Çatışmaların ortaya çıkması kaçınılmazdır; bir paydaş grubu, özyinelemeli fragment'larda olduğu gibi diğer paydaşlar için verimsiz hatta zararlı olan bir özelliğe ihtiyaç duyar. Durumu iyileştirmek ve tatmin olmayan paydaşların GraphQL'den hayal kırıklığına uğramamasını sağlamak için ne yapılabilir?

Spec'in bir özelliği "devre dışı bırakma" imkânı sunabileceğini, ne yaptıklarını bilen yöneticilerin kendi gereksinimlerini karşılamak adına bazı kısıtlamaları kaldırmasına olanak tanıyabileceğini savundum. Şimdi, ben bu çözüme katılmıyorum; ancak yine de bu tartışmanın yapılması gerektiğinden açıkça ortaya koyuyorum. Bu fikir tartışmalı olduğundan, daha iyi bir alternatif, GraphQL sunucularının açıkça etkinleştirilmesi gereken özel özellikler aracılığıyla bu davranışı sağlamasıdır.


Bültenimize abone olun

Gato GraphQL'deki tüm güncellemelerden haberdar olun.