GraphQL Çözümleyicileri: En İyi Uygulamalar

Graphql.org sitesinden

Bu gönderi, PayPal'da GraphQL API'leri oluştururken yaptığımız bir dizi en iyi uygulama ve gözlemin ilk bölümüdür. Gelecek gönderilerde, şema tasarımı, hata yönetimi, üretim görünürlüğü, müşteri tarafı entegrasyonlarını optimize etme ve ekipler için takım oluşturma konusundaki düşüncelerimizi paylaşacağız.

PayPal'ın REST'ten GraphQL'e olan yolculuğu hakkında daha önce yayınlanan “GraphQL: PayPal Checkout için bir başarı öyküsü” yayınımızı görmüş olabilirsiniz. Bu gönderi, zaman içinde hızlı, test edilebilir ve esnek olan çözümleyiciler oluşturmak için bazı en iyi uygulamaları ayrıntılandırıyor.

Çözümleyici nedir?

Aynı temelden başlayalım. Çözümleyici nedir?

Çözücü tanımı
Her türdeki her alan, çözücü olarak adlandırılan bir işlevle desteklenir.

Çözümleyici, bir şemadaki tür veya alan için bir değeri çözümleyen bir işlevdir. Çözümleyiciler, Dizeler, Sayılar, Booleans, vb. Gibi nesneleri veya skalaları döndürebilirler. Bir Nesne döndürülürse, yürütme bir sonraki alt alana devam eder. Bir skaler döndürülürse (tipik olarak bir yaprak düğümünde), yürütme tamamlanır. Boş değer döndürülürse, yürütme durur ve devam etmez.

Çözücüler de asenkron olabilir! Başka bir REST API, veritabanı, önbellek, sabit vb. Değerleri çözebilirler.

Daha sonra, hızlı, test edilebilir ve esnek çözümleyicilerin nasıl oluşturulacağını gösteren bir dizi örnek üzerinde yürüyeceğiz.

Sorguları yürütme

Çözümleyicileri daha iyi anlamak için sorguların nasıl yürütüldüğünü bilmeniz gerekir.

Her GraphQL sorgusu üç aşamadan geçer. Sorgular ayrıştırılır, doğrulanır ve yürütülür.

  1. Ayrıştırma - Bir sorgu, soyut bir sözdizimi ağacına (veya AST) ayrıştırılır. AST'ler inanılmaz derecede güçlüdür ve ESLint, babel, vb. Gibi araçların arkasındadır. Bir GraphQL'in nasıl göründüğünü görmek istiyorsanız, astexplorer.net adresini ziyaret edin ve JavaScript'i GraphQL olarak değiştirin. Solda bir sorgu ve sağda bir AST göreceksiniz.
  2. Doğrula - AST şemaya karşı onaylanmıştır. Doğru sorgu sözdizimini ve alanların olup olmadığını kontrol eder.
  3. Yürüt - Çalışma zamanı ağacın kökünden başlayarak AST içinde yürür, çözümleyiciler çağırır, sonuçları toplar ve JSON yayar.

Bu örnek için, bu sorguyu inceleyeceğiz:

Daha sonra başvurmak için sorgula

Bu sorgu ayrıştırıldığında, bir AST'ye veya bir ağaca dönüştürülür.

Ağaç olarak temsil edilen sorgu

Kök Sorgu türü, ağacın giriş noktasıdır ve iki kök alanımızı içerir: kullanıcı ve albüm. Kullanıcı ve albüm çözümleyicileri paralel olarak yürütülür (tüm çalışma zamanları arasında tipiktir). Ağaç ilk önce yürütülür, yani kullanıcının adı ve e-postası çalıştırılmadan önce kullanıcının çözülmesi gerekir. Kullanıcı çözümleyicisi eşzamansız ise, kullanıcı dalı çözümleninceye kadar erteler. Tüm yaprak düğümleri, ad, e-posta, başlık çözüldükten sonra yürütme tamamlanır.

Kullanıcı ve albüm gibi Kök Sorgu alanları, paralel olarak, ancak belirli bir sırada yürütülmez. Genellikle, alanlar sorguda göründükleri sırayla çalıştırılır, ancak bunu varsaymak güvenli değildir. Alanlar paralel olarak yürütüldüğünden, atomik, idempotent ve yan etkisiz olduğu varsayılmaktadır.

Çözücülere daha yakından bakmak

Sonraki birkaç bölümde, JavaScript kullanacağız, ancak GraphQL sunucuları hemen hemen her dilde yazılabilir.

Dört argümanlı çözümleyiciler - kök, args, bağlam, bilgi

Bir biçimde veya başka bir şekilde, her dilde her çözücü bu dört argümanı alır:

  • root - Önceki / ebeveyn türünden sonuç
  • args - Alana sağlanan argümanlar
  • bağlamı - tüm çözümleyicilere sağlanan bir değiştirilebilir nesne
  • info - Sorgu ile ilgili alana özgü bilgiler (nadiren kullanılır)

Bu dört argüman verinin çözücüler arasında nasıl aktığını anlamak için temeldir.

Varsayılan çözücüler

Devam etmeden önce, bir GraphQL sunucusunun yerleşik varsayılan çözücülerine sahip olduğunu belirtmekte fayda vardır, bu nedenle her alan için bir çözümleyici işlevi belirtmeniz gerekmez. Varsayılan bir çözümleyici, alanla aynı ada sahip bir özellik bulmak için kökte arama yapar. Bir uygulama muhtemelen şöyle görünür:

Varsayılan çözümleyici uygulaması

Çözücülerde veri alma

Veriyi nereden almalıyız? Seçeneklerimizle olan takaslar nelerdir?

Sonraki birkaç örnekte, bu şemaya geri döneceğiz:

Bir etkinlik alanı gerekli bir id argümanına sahip, bir Etkinlik döndürür

Çözücüler arasında veri aktarımı

bağlam, tüm çözümleyicilere sağlanan değiştirilebilir bir nesnedir. Her istek arasında yaratıldı ve yok edildi. Ortak Kimlik Bilgileri, API'ler ve veritabanları için ortak modeller / alıcılar, vb. Depolamak için harika bir yer. PayPal'da Express'te inşa edilmiş altyapıya sahip büyük bir Node.js mağazasıyız.

İlk önce bağlam hakkında bilgi edindiğinizde, ilk düşünce bağlamı genel amaçlı önbellek olarak kullanmak olabilir. Bu önerilmez, ancak burada bir uygulamanın nasıl göründüğü gösterilmektedir.

Bağlam kullanarak çözücüler arasında veri aktarımı. Bu önerilmez!

Başlık çağrıldığında, olay sonucunu bağlamda saklarız. PhotoUrl çağrıldığında olayı bağlam dışına çıkarır ve kullanırız. Bu kod güvenilir değil. Bu unvanın photoUrl'den önce çalıştırılacağının garantisi yoktur.

Olayın bağlamda olup olmadığını kontrol etmek için her iki çözücüyü de düzeltebiliriz. Eğer öyleyse, onu kullan. Aksi takdirde, onu alır ve daha sonra saklarız, ancak hatalar için hala geniş bir yüzey alanı vardır.

Bunun yerine, çözümleyicilerin içindeki içeriği değiştirmekten kaçınmalıyız. Bilgilerin ve kaygıların birbiriyle karışmasını engellemeliyiz ki çözücülerimizin anlaşılması, hata ayıklaması ve test edilmesi kolaydır.

Ebeveyn-çocuğa veri aktarma

Kök argüman, verileri ana çözümleyiciden alt çözümleyicilere aktarmak içindir.

Örneğin, tüm Etkinlik alanlarının bulunduğu bir Etkinlik türü oluşturuyorsanız,
aynı verilere bağlı olarak, etkinlik alanında bir kez almak isteyebilirsiniz,
Etkinlik'in her alanında değil.

İyi bir fikir gibi görünüyor değil mi? Bu, bina çözücülere başlamanın hızlı bir yoludur, ancak bazı sorunlarla karşılaşabilirsiniz. Nedenini anlayalım.

Aşağıdaki örnekler için iki alana sahip bir Etkinlik türüyle çalışacağız.

İki alanlı etkinlik türü: title ve photoUrl

Etkinlik alanlarının çoğu, bir Etkinlik API'sinden alınabilir, bu yüzden onu en üst düzey etkinlik çözümleyiciden alabiliriz ve sonuçları başlığımıza ve photoUrl çözümleyicilerimize sağlayabiliriz.

Üst düzey olay çözümleyici veri alır, başlık ve photoUrl alan çözümleyicileri için sonuçlar sağlar

Daha da iyisi, alttaki iki çözümleyiciyi belirtmemize gerek yok.
Varsayılan çözümleyicileri kullanabiliriz, çünkü nesne getEvent () tarafından döndürüldü
bir başlık ve photoUrl özelliğine sahiptir.

id ve başlık varsayılan çözücüler kullanılarak çözüldü

Bunun nesi yanlış?

Overfeting ile karşılaşabileceğiniz iki senaryo var.

Senaryo # 1: Çok katmanlı veri alma

Bazı gereksinimlerin geldiğini ve bir etkinliğin katılımcılarını göstermeniz gerektiğini varsayalım. Event'e bir katılımcı alanı ekleyerek başlıyoruz.

Ek katılımcı alanlı etkinlik türü

Katılımcı ayrıntılarını aldığınızda, iki seçeneğiniz vardır: bu verileri etkinlik çözümleyicide veya katılımcı çözümleyicide alın.

İlk seçeneği test edeceğiz: bunu olay çözümleyicisine ekleyerek.

etkinlik çözücüsü, etkinlik ayrıntılarını ve katılımcı ayrıntılarını getiren iki API'yi çağırır

Bir müşteri yalnızca başlık ve photoUrl için sorgularsa ancak katılımcılar sormazsa.Şimdi verimsiz olursunuz ve Katılanlar API'nıza gereksiz bir istek yapıyorsunuzdur.

Bu senin hatan değil, işte böyle çalışıyoruz. Modelleri tanır ve kopyalarız.
Katkıda bulunanlar olay çözümleyicide veri alımının yapıldığını görürse, büyük olasılıkla
Çok fazla düşünmeden oraya herhangi bir ek veri ekle.

Katılımcıları katılımcı çözücüsünün içine getirerek test etmek için bir seçeneğimiz daha var.

katılımcı çözücüsü, Katılımcı API'sından katılımcıların ayrıntılarını alır

Müvekkilimiz sadece katılımcılar için soru sorarsa, title ve photoUrl değil. Events API’mıza gereksiz bir istek yaparak hala yetersiz kalıyoruz.

Senaryo # 2: N + 1 Sorunu

Veriler alan düzeyinde toplandığından, fazla tarama riski taşıyoruz. Overfetching ve N + 1 problemi GraphQL dünyasında popüler bir konudur. Shopify, N + 1'i iyi açıklayan harika bir makaleye sahip.

Bu bizi burada nasıl etkiler?

Daha iyi göstermek için, tüm olayları döndüren yeni bir olay alanı ekleyeceğiz.

Bir etkinlik alanı tüm etkinlikleri döndürür.Başlığı ve katılımcıları ile tüm olaylar için sorgu

Bir müşteri tüm olaylar ve katılımcılarını sorgularsa, katılımcılar birden fazla etkinliğe katılabileceği için fazla alım riskini üstleniriz. Aynı katılımcı için çift talepte bulunabiliriz.

Bu sorun, isteklerin etkilenebileceği ve sisteminiz üzerinde gereksiz baskıya yol açabileceği büyük bir organizasyonda güçlendirilir.

Bunu çözmek için, toplu iş taleplerini ayırmamız gerekiyor!

JavaScript'te, bazı popüler seçenekler veri yükleyici ve Apollo veri kaynaklarıdır.

Başka bir dil kullanıyorsanız, alabileceğiniz bir şey olabilir. Bunu kendi başınıza çözmeden önce etrafınıza bir göz atın.

Bunun temelinde, bu kütüphaneler veri erişim katmanınızın üstüne oturur ve geri bırakma veya not alma kullanarak giden istekleri önbelleğe alır ve ayırır. Zaman uyumsuzluğunun nasıl göründüğünü merak ediyorsanız, Daniel Brain’in mükemmel yayınına göz atın!

Alan düzeyinde veri alma

Daha önce, “üst-ağır” ebeveyn-çocuk çözücülere aşırı dokunmakla yanmanın kolay olduğunu gördük.

Daha iyi bir alternatif var mı?

Ebeveyn-çocuk seçeneğini tekrar açıklayalım. Ya çocuk alanlarımızın kendi verilerini almaktan sorumlu olması için bunu tersine çevirirsek?

Alanlar kendi veri toplama işlemlerinden sorumludur.
Bu neden daha iyi bir alternatif?

Bu kod hakkında sebep kolaydır. Bir e-postanın nereden alındığını tam olarak biliyorsunuz. Bu kolay hata ayıklama için yapar.

Bu kod daha fazla test edilebilir. Başlık çözümleyiciyi gerçekten test etmek istediğinizde olay çözümleyicisini test etmeniz gerekmez.

Bazıları için, getEvent çoğaltması kod kokusu gibi görünebilir. Ancak, basit, anlaşılması kolay ve daha test edilebilir bir koda sahip olmak, çoğaltılmaya değer.

Fakat burada hala potansiyel bir sorun var. Bir müşteri title ve photoUrl için sorgularsa, Event API’mizde getEvent ile bir istek daha yaratırız. N + 1 probleminde daha önce gördüğümüz gibi, dataloader ve Apollo veri kaynakları gibi kütüphaneleri kullanarak istekleri bir çerçeve düzeyinde ayırmalıyız.

Alan düzeyinde ve veri tekilleştirme isteklerinde veri alırsak, hata ayıklaması ve test edilmesi kolay olan bir kodumuz olur ve düşünmeden en iyi şekilde veri alabiliriz.

En iyi uygulamalar

  • Ebeveynlerden çocuğa veri alma ve aktarma, dikkatli bir şekilde kullanılmalıdır.
  • Aşağı akış isteklerini ayırmak için dataloader gibi kitaplıkları kullanın.
  • Veri kaynaklarınız üzerinde neden olduğunuz baskıların farkında olun.
  • “Bağlam” değiştirmeyin. Tutarlı, daha az buggy kodu sağlar.
  • Okunabilir, bakımı yapılabilir, test edilebilir çözücüler yazın. Çok akıllıca değil.
  • Çözücülerinizi mümkün olduğunca ince yapın. Yeniden kullanılabilir zaman uyumsuzluk işlevlerine veri alma mantığı ayıklayın.

Bizi izlemeye devam edin!

Düşünceler? Ekibinizin en iyi uygulamaları ve bina çözücülerle ilgili öğrenmelerini duymayı çok isteriz. Bu sıkça tartışılmayan bir konu, ancak uzun ömürlü GraphQL API'leri oluşturmak için önemlidir.

Gelecek gönderilerde, şema tasarımı, hata yönetimi, üretim görünürlüğü, müşteri tarafı entegrasyonlarını optimize etme ve ekipler için takım oluşturma konusundaki düşüncelerimizi paylaşacağız.

İşe alıyoruz! Front Ön uç alt yapı, GraphQL veya PayPal'da React üzerinde çalışmak isterseniz, beni Twitter'da @ mark_stuart'da DM!