Bir GraphQL Primer: Neden Yeni Bir API Türüne İhtiyacımız Var (Bölüm 1)

Yayınlanan: 2022-03-10
Hızlı özet ↬ Önce özellikleri keşfetmek yerine, onları bağlama oturtmak ve nasıl var olduklarını anlamak yardımcı olur. GraphQL'ye giriş ve son 60 yıllık API geliştirmesinden öğrenilen dersler.

Bu seride sizi GraphQL ile tanıştırmak istiyorum. Sonunda, sadece ne olduğunu değil, aynı zamanda kökenlerini, sakıncalarını ve onunla nasıl çalışılacağının temellerini de anlamalısınız. Bu ilk makalede, uygulamaya atlamak yerine, RPC'den bugüne son 60 yıllık API geliştirmesinden öğrenilen derslere bakarak GraphQL'ye (ve benzer araçlara) nasıl ve neden ulaştığımızı gözden geçirmek istiyorum. Ne de olsa Mark Twain'in renkli bir şekilde tanımladığı gibi, yeni fikir yoktur.

"Yeni fikir diye bir şey yoktur. İmkansız. Bir sürü eski fikri alıp onları bir tür zihinsel kaleydoskopa koyuyoruz."

— Mark Twain, "Mark Twain'in Kendi Otobiyografisi: Kuzey Amerika İncelemesinden Bölümler"de

Ama önce odadaki file hitap etmeliyim. Yeni şeyler her zaman heyecan vericidir ama aynı zamanda yorucu da olabilirler. GraphQL'i duymuş olabilirsiniz ve kendi kendinize şöyle düşündünüz: “Neden…” Alternatif olarak, belki daha çok, “Neden yeni bir API tasarım trendini umursuyorum? REST… iyi.” Bunlar meşru sorular, bu yüzden neden buna dikkat etmeniz gerektiğini açıklamama yardım edeyim.

Tanıtım

Ekibinize yeni araçlar getirmenin faydaları, maliyetlerine karşı tartılmalıdır. Ölçülecek çok şey var. Öğrenmek için gereken zaman vardır, dönüştürme zamanını özellik geliştirmeden alır, iki sistemin bakımının ek yükü. Bu kadar yüksek maliyetlerle, herhangi bir yeni teknolojinin çok daha iyi, daha hızlı veya daha üretken olması gerekir . Artımlı iyileştirmeler, heyecan verici olsa da, yatırıma değmez. Bahsetmek istediğim API türleri, özellikle GraphQL, bence ileriye doğru atılmış büyük bir adım ve maliyeti haklı çıkarmak için fazlasıyla fayda sağlıyor.

Önce özellikleri keşfetmek yerine, onları bir bağlama oturtmak ve nasıl var olduklarını anlamak yardımcı olur. Bunu yapmak için, API'lerin geçmişinin biraz özetiyle başlayacağım.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

RPC

RPC, tartışmasız ilk büyük API modeliydi ve kökenleri, 60'ların ortalarındaki erken hesaplamaya kadar uzanıyor. O zamanlar bilgisayarlar hala o kadar büyük ve pahalıydı ki, API güdümlü uygulama geliştirme fikri, bizim düşündüğümüz gibi, çoğunlukla sadece teorikti. Bant genişliği/gecikme, hesaplama gücü, paylaşılan bilgi işlem süresi ve fiziksel yakınlık gibi kısıtlamalar, mühendisleri verileri açığa çıkaran hizmetler yerine dağıtılmış sistemler açısından düşünmeye zorladı . 60'lardaki ARPANET'ten, CORBA ve Java'nın RMI'si gibi şeylerle 90'ların ortalarına kadar, çoğu bilgisayar, bir istemcinin bir prosedüre neden olduğu bir istemci-sunucu etkileşim modeli olan Uzaktan Yordam Çağrılarını (RPC) kullanarak birbirleriyle etkileşime girdi. (veya yöntem) uzak bir sunucuda yürütülür.

RPC hakkında çok güzel şeyler var. Temel ilkesi, bir geliştiricinin uzak bir ortamda kodu yerel bir ortamdaymış gibi ele almasına izin vermektir, ancak çok daha yavaş ve daha az güvenilirdir, aksi takdirde farklı ve farklı sistemlerde süreklilik yaratır. ARPANET'ten çıkan pek çok şey gibi, bu tür bir süreklilik, DB erişimi ve harici hizmet çağrıları gibi güvenilmez ve asenkron eylemlerle çalışırken hala uğraştığımız bir şey olduğundan, zamanının ötesindeydi.

On yıllar boyunca, geliştiricilerin bunun gibi eşzamansız davranışları bir programın tipik akışına yerleştirmelerine nasıl izin verileceği konusunda muazzam miktarda araştırma yapıldı; O sırada Vaatler, Vadeli İşlemler ve Zamanlanmış Görevler gibi şeyler mevcut olsaydı, API ortamımızın farklı görünmesi olasıydı.

RPC ile ilgili bir başka harika şey de, veri yapısı tarafından kısıtlanmadığından, minimum ağ ek yükü ve daha küçük yüklerle sonuçlanabilecek tam olarak ihtiyaç duyulan bilgileri talep eden ve alan istemciler için son derece özel yöntemler yazılabilir.

Ancak, RPC'yi zorlaştıran şeyler var. İlk olarak, süreklilik bağlam gerektirir . RPC, tasarımı gereği, yerel ve uzak sistemler arasında oldukça fazla bağlantı oluşturur; yerel ve uzak kodunuz arasındaki sınırları kaybedersiniz. Bazı alan adları için, istemci SDK'larında olduğu gibi bu sorun olmaz ve hatta tercih edilir, ancak istemci kodunun iyi anlaşılmadığı API'ler için, daha fazla veri odaklı bir şeye göre çok daha az esnek olabilir.

Daha da önemlisi , API yöntemlerinin çoğalma potansiyelidir . Teoride, bir RPC hizmeti, herhangi bir görevi yerine getirebilecek küçük, düşünceli bir API sunar. Uygulamada, çok sayıda harici uç nokta, fazla yapı olmadan birikebilir. Ekip üyeleri gelip giderken ve projeler değiştikçe API'lerin çakışmasını ve zaman içinde tekrarlamayı önlemek çok büyük bir disiplin gerektirir.

Bahsettiğim gibi uygun alet ve dokümantasyon değişikliklerinin yönetilebileceği doğru ama benim zamanımda yazılım yazarken çok az sayıda otomatik dokümantasyon ve disiplinli hizmetle karşılaştım, bu yüzden bana göre bu biraz zor. kırmızı ringa.

SABUN

Bir sonraki büyük API türü, 90'ların sonlarında Microsoft Research'te doğan SOAP idi. SOAP ( Basit Nesne Erişim Protokolü ), uygulamalar arasında XML tabanlı iletişim için iddialı bir protokol özelliğidir. SOAP'ın belirtilen amacı, karmaşık web hizmetleri için iyi yapılandırılmış bir temel oluşturarak RPC'nin, özellikle XML-RPC'nin bazı pratik dezavantajlarını ele almaktı. Aslında bu, XML'e bir davranışsal tip sistemi eklemek anlamına geliyordu. Ne yazık ki, bugün çok az sayıda yeni SOAP bitiş noktasının yazılması gerçeğinin kanıtladığı gibi, çözdüğünden daha fazla engel yarattı.

"SABUN, çoğu insanın ılımlı bir başarı olarak kabul edeceği şeydir."

— Don Kutusu

SOAP, dayanılmaz ayrıntılarına ve korkunç isimlerine rağmen, bunun için bazı iyi şeylere sahipti. WSDL ve WADL'deki ("wizdle" ve "waddle" olarak telaffuz edilir) istemci ve sunucu arasındaki uygulanabilir sözleşmeler, öngörülebilir, tür açısından güvenli sonuçları garanti eder ve WSDL, dokümantasyon oluşturmak veya IDE'ler ve diğer araçlarla entegrasyonlar oluşturmak için kullanılabilir.

API evrimiyle ilgili SOAP'ın büyük keşfi, daha fazla kaynak odaklı çağrıların kademeli ve muhtemelen kasıtsız olarak tanıtılmasıydı. SOAP uç noktaları, verileri oluşturmak için gerekli yöntemleri (bu şekilde yazıldığını varsayarak) düşünmek yerine önceden belirlenmiş bir yapıya sahip verileri talep etmenize olanak tanır.

SOAP'ın en önemli dezavantajı, çok ayrıntılı olmasıdır; çok sayıda alet kullanmadan kullanmak neredeyse imkansızdır . Testler yazmak için araçlara, bir sunucudan gelen yanıtları incelemek için araçlara ve tüm verileri ayrıştırmak için araçlara ihtiyacınız var. Birçok eski sistem hala SOAP kullanıyor, ancak araç gereksinimi, çoğu yeni proje için onu çok hantal hale getiriyor ve XML yapısı için gereken bayt sayısı, mobil cihazlara veya konuşkan dağıtılmış sistemlere hizmet vermek için onu kötü bir seçim haline getiriyor.

Daha fazla bilgi için, orijinal ekip üyelerinden biri olan Don Box'tan SOAP spesifikasyonunu ve şaşırtıcı derecede ilginç SOAP tarihini okumaya değer.

DİNLENMEK

Son olarak, API tasarım desenine geldik du jour: REST. 2000 yılında Roy Fielding tarafından doktora tezinde tanıtılan REST, sarkacı tamamen farklı bir yöne çevirdi. REST, birçok yönden SOAP'ın antitezi ve onlara yan yana bakmak, tezinin biraz öfkeli bir çıkış olduğunu hissettiriyor.

SOAP, HTTP'yi aptal bir aktarım olarak kullanır ve yapısını istek ve yanıt gövdesinde oluşturur. Öte yandan REST, istemci-sunucu sözleşmelerini, araçları, XML ve ısmarlama başlıkları atar ve HTTP fiillerini kullanmak yerine yapıyı seçtiğinden, bazı hiyerarşilerde bir kaynağa başvuran URI'ler ve verilerle etkileşime giren yapı olduğundan, bunları HTTP semantiği ile değiştirir. veri.

SABUN DİNLENMEK
HTTP Fiilleri AL, KOY, YAYINLA, YAMA, SİL
Veri formatı XML Ne istersen
İstemci/Sunucu Sözleşmeleri Her gün her gün! bunlara kimin ihtiyacı var
Tip Sistem JavaScript'in imzasız kısa hakkı var mı?
URL'ler İşlemleri tanımlayın Adlandırılmış kaynaklar

REST, API tasarımını etkileşimleri modellemekten yalnızca bir etki alanının verilerini modellemeye tamamen ve açıkça değiştirir. Bir REST API ile çalışırken tamamen kaynak odaklı olmak, belirli bir veri parçasını almak için ne gerektiğini artık bilmenize veya ilgilenmenize gerek yok; ne de arka uç hizmetlerinin uygulanması hakkında herhangi bir şey bilmeniz gerekmez.

Basitlik yalnızca geliştiriciler için bir nimet değildi, aynı zamanda URL'ler sabit bilgileri temsil ettiğinden kolayca önbelleğe alınabilir, durumsuz olması yatay olarak ölçeklendirmeyi kolaylaştırır ve tüketicilerin ihtiyaçlarını tahmin etmek yerine verileri modellediği için API'lerin yüzey alanını önemli ölçüde azaltabilir. .

REST harikadır ve her yerde bulunması şaşırtıcı bir başarıdır, ancak kendisinden önce gelen tüm çözümler gibi REST de kusursuz değildir. Bazı eksiklikleri hakkında somut olarak konuşmak için, temel bir örnek üzerinden gidelim. Diyelim ki blog gönderilerinin bir listesini ve yazarlarının adını görüntüleyen bir blogun açılış sayfasını oluşturmamız gerekiyor.

Temel bir blogun ana sayfası
Her gönderinin başlığını ve yazarını gösteren temel bir blogun ana sayfası. (Büyük önizleme)

Düz bir REST API'sinden ana sayfa verilerini alabilecek kodu yazalım. Kaynaklarımızı saran birkaç işlevle başlayacağız.

 const getPosts = () => fetch(`${API_ROOT}/posts`); const getPost = postId => fetch(`${API_ROOT}/posts/${postId}`); const getAuthor = authorId => fetch(`${API_ROOT}/authors/${authorId}`);

Şimdi orkestrasyon yapalım!

 const getPostWithAuthor = postId => { return getPost(postId) .then(post => getAuthor(post.author)) .then(author => { return Object.assign({}, post, { author }) }) }; const getHomePageData = () => { return getPosts() .then(postIds => { const postDetails = postIds.map(getPostWithAuthor); return Promise.all(postDetails); }) };

Yani kodumuz şunları yapacak:

  • Tüm Gönderileri Al;
  • Her Gönderiyle ilgili ayrıntıları alın;
  • Her Gönderi için Yazar kaynağını getir.

İşin güzel yanı, bunun üzerinde akıl yürütmenin oldukça kolay olması, iyi organize edilmiş olması ve her kaynağın kavramsal sınırlarının iyi çizilmiş olmasıdır. Buradaki serseri, çoğu seri olarak gerçekleşen sekiz ağ isteğinde bulunmamızdır.

 GET /posts GET /posts/234 GET /posts/456 GET /posts/17 GET /posts/156 GET /author/9 GET /author/4 GET /author/7 GET /author/2

Evet, API'nin sayfalandırılmış /posts bitiş noktasına sahip olabileceğini, ancak bunun tüyleri böldüğünü öne sürerek bu örneği eleştirebilirsiniz. Gerçek şu ki, eksiksiz bir uygulama veya sayfa oluşturmak için genellikle birbirine bağlı hale getirmek için bir API çağrıları koleksiyonunuz var.

REST istemcileri ve sunucuları geliştirmek kesinlikle öncekinden daha iyidir veya en azından daha aptalca kanıtlardır, ancak Fielding'in makalesinden bu yana geçen yirmi yılda çok şey değişti. O zamanlar tüm bilgisayarlar bej plastiktendi; şimdi onlar alüminyum! Cidden, 2000 yılı kişisel bilgisayardaki patlamanın zirvesine yakındı. Her yıl işlemcilerin hızı ikiye katlanıyor ve ağlar inanılmaz bir hızla hızlanıyordu. İnternetin pazar penetrasyonu %45 civarındaydı ve gidecek başka bir yer yoktu.

Ardından, 2008 civarında mobil bilgi işlem ana akım haline geldi. Mobil ile, bir gecede hız/performans açısından on yılı etkili bir şekilde geri aldık. 2017'de yaklaşık %80 yurt içi ve %50'den fazla küresel akıllı telefon penetrasyonumuz var ve API tasarımıyla ilgili bazı varsayımlarımızı yeniden düşünmenin zamanı geldi.

REST'in Zayıf Yönleri

Aşağıda, özellikle mobilde çalışan bir istemci uygulama geliştiricisi açısından REST'e eleştirel bir bakış sunulmaktadır. GraphQL ve GraphQL tarzı API'ler yeni değildir ve REST geliştiricilerinin kavrayışının dışında kalan sorunları çözmez. GraphQL'nin en önemli katkısı, bu sorunları sistematik olarak ve başka yerde kolayca bulunamayan bir entegrasyon düzeyiyle çözme yeteneğidir. Başka bir deyişle, “piller dahil” bir çözümdür.

Fielding de dahil olmak üzere REST'in başlıca yazarları, 2017'nin sonlarında (REST Architectural Style Üzerine Düşünceler ve “Modern Web Mimarisinin İlke Tasarımı”) REST'in yirmi yılı ve onun ilham aldığı birçok modeli yansıtan bir makale yayınladı. API tasarımıyla ilgilenen herkes için kısa ve kesinlikle okumaya değer.

Biraz tarihsel bağlam ve bir referans uygulamasıyla, REST'in üç ana zayıflığına bakalım.

REST Konuşkandır

REST hizmetleri, bir uygulamayı oluşturmak için yeterli veriyi elde etmek için istemci ve sunucu arasında birden fazla gidiş-dönüş gerektiğinden, en azından biraz "geveze" olma eğilimindedir. Bu istekler dizisi, özellikle mobil cihazlarda yıkıcı performans etkilerine sahiptir. Blog örneğine geri dönersek, yeni bir telefon ve 4G bağlantılı güvenilir ağ ile en iyi senaryoda bile, ilk veri baytı indirilmeden önce yalnızca gecikme ek yükü için neredeyse 0,5 saniye harcadınız.

55 ms 4G gecikme * 8 istek = 440 ms ek yük

Uygulama performansına kullanıcı yanıtını gösteren grafik
Çeşitli uygulama performansı düzeylerine kullanıcı yanıtını açıklayan bir grafik. (İmaj kredisi: Yüksek Performanslı Tarayıcı Ağı) (Geniş önizleme)

Sohbet servisleriyle ilgili bir başka sorun da, çoğu durumda büyük bir isteğin indirilmesinin birçok küçük istekten daha az zaman almasıdır. Küçük isteklerin düşük performansı, TCP Yavaş Başlatma, başlık sıkıştırma eksikliği ve gzip verimliliği gibi birçok nedenden dolayı doğrudur ve bunu merak ediyorsanız, Ilya Grigorik'in Yüksek Performanslı Tarayıcı Ağı'nı okumanızı şiddetle tavsiye ederim. MaxCDN blogu ayrıca harika bir genel bakışa sahiptir.

Bu sorun teknik olarak REST ile değil, HTTP ile, özellikle HTTP/1 ile ilgilidir. HTTP/2, API stilinden bağımsız olarak sohbet sorununu çözer, ancak tarayıcılar ve yerel SDK'lar gibi istemcilerde geniş bir desteğe sahiptir. Ne yazık ki, API tarafında sunum yavaş oldu. En iyi 10 bin web sitesi arasında benimsenme, 2017'nin sonları itibarıyla yaklaşık %20 (ve giderek artıyor). Yeteneğiniz varsa, lütfen altyapınızı güncelleyin! Bu arada, bu denklemin sadece bir kısmı olduğu için üzerinde durmayalım.

HTTP bir yana, sohbetin neden önemli olduğunun son parçası, mobil cihazların ve özellikle telsizlerinin nasıl çalıştığıyla ilgilidir. Uzun ve kısa, radyoyu çalıştırmanın bir telefonun pil açısından en yoğun bölümlerinden biri olması, bu nedenle işletim sisteminin her fırsatta onu kapatması. Telsizi başlatmak yalnızca pili boşaltmakla kalmaz, aynı zamanda her isteğe daha fazla ek yük getirir.

TMI (Fazla Getirme)

REST tarzı hizmetlerle ilgili bir sonraki sorun, göndermenin gerekenden daha fazla bilgi olmasıdır. Blog örneğimizde, ihtiyacımız olan tek şey, her gönderinin başlığı ve döndürülenlerin yalnızca yaklaşık %17'si olan yazarının adıdır. Bu, çok basit bir yük için 6 kat kayıptır. Gerçek dünyadaki bir API'de, bu tür bir ek yük çok büyük olabilir. Örneğin e-ticaret siteleri, genellikle tek bir ürünü binlerce JSON satırı olarak temsil eder. Sohbet sorunu gibi, REST hizmetleri de bugün bu senaryoyu, verilerin bölümlerini koşullu olarak dahil etmek veya hariç tutmak için "seyrek alan kümeleri" kullanarak halledebilir. Ne yazık ki, bunun için destek, ağ önbelleğe alma için sivilceli, eksik veya sorunlu.

Takım ve Introspection

REST API'lerinde eksik olan son şey, iç gözlem mekanizmalarıdır. Bir uç noktanın dönüş türleri veya yapısı hakkında bilgi içeren herhangi bir sözleşme olmadan, güvenilir bir şekilde dokümantasyon oluşturmanın, araçlar oluşturmanın veya verilerle etkileşim kurmanın hiçbir yolu yoktur. Bu sorunu değişen derecelerde çözmek için REST içinde çalışmak mümkündür. OpenAPI, OData veya JSON API'sini tam olarak uygulayan projeler genellikle temizdir, iyi tanımlanmıştır ve çeşitli kapsamlarda iyi belgelenmiştir ancak bunun gibi arka uçlar nadirdir. Nispeten düşük asılı bir meyve olan Hypermedia bile, onlarca yıldır konferans görüşmelerinde lanse edilmesine rağmen, hala sık sık iyi yapılmamaktadır.

Çözüm

API türlerinin her biri kusurludur, ancak her kalıp kusurludur. Bu yazı, yazılım devlerinin ortaya koyduğu olağanüstü temelin bir yargısı değil, yalnızca bu kalıpların her birinin, bir istemci geliştiricinin perspektifinden "saf" formlarında uygulanan ayık bir değerlendirmesidir. Umarım bu düşünceden uzaklaşmak yerine, REST veya RPC gibi bir model bozulur, her birinin nasıl değiş tokuş yaptığını ve bir mühendislik kuruluşunun kendi API'lerini geliştirmek için çabalarına odaklanabileceği alanları düşünerek uzaklaşabilirsiniz.

Bir sonraki makalede, GraphQL'i ve yukarıda bahsettiğim bazı sorunları nasıl çözmeyi amaçladığını inceleyeceğim. GraphQL ve benzeri araçlardaki yenilik, uygulamalarında değil, entegrasyon seviyelerindedir. Lütfen, siz veya ekibiniz "pil dahil" bir API aramıyorsanız, bugün daha güçlü bir temel oluşturmaya yardımcı olabilecek yeni OpenAPI spesifikasyonu gibi bir şeye bakmayı düşünün!

Bu makaleyi beğendiyseniz (veya nefret ettiyseniz) ve bana geri bildirimde bulunmak istiyorsanız, lütfen beni Twitter'da @ebaerbaerbaer veya LinkedIn'de ericjbaer olarak bulun.