Protobuf'tan GraphQL'e Dönüştürücü
Bir .proto şeması yapıştırın. Type'lar, enum'lar, null olmayan listeler ve map alanları için sentetik entry tipleri içeren bir GraphQL SDL alın — bir gateway şemasını başlatmaya hazır.
Girdi (.proto şeması)
Çıktı (GraphQL SDL)
Bu araç ne yapıyor
Elinizde bir Protocol Buffers şeması ve önünde bir GraphQL gateway’i var (ya da kurmak üzeresiniz). Google’ın rejoiner gibi araçlar proto mesajlarını çalışma anında canlı bir GraphQL endpoint’ine bağlıyor; ama şemayı taslaklamak, kod incelemesi yapmak ya da elle yazılmış bir resolver katmanını başlatmak için çoğu zaman SDL’in nasıl göründüğünü görmek isterseniz yeter. Bu dönüştürücü tam da bunu tarayıcınızda yapar — yapıştır, çıktıyı kopyala, schema.graphql dosyasına bırak.
Her message bir GraphQL type’ı olur; her enum bir GraphQL enum’u olur. Alan adları snake_case (proto geleneği) yerine camelCase’e çevrilir — bu, GraphQL spesifikasyonunda yer alan ve her linter tarafından dayatılan gelenektir. Skalerler ve enum’lar non-null olarak yazılır (String!, OrderStatus!); çünkü proto3 alanları telde her zaman bir değer taşır — atanmamış olanlar bile sıfır değerine düşer. Tek değerli iç içe mesajlar nullable olarak yazılır, bu da proto3’ün has-value semantiğine uyar.
repeated alanlar [T!]! olarak — null olmayan elemanlardan oluşan null olmayan liste — render edilir, çünkü proto3’te repeated alanlar asla null olmaz ve null girdi içermez. Map’ler için bir geçici çözüm gerekir: GraphQL’de yerel map tipi yok, dolayısıyla metadata alanındaki map<string, string>, sentetik bir type OrderMetadataEntry { key: String! value: String! } ile birlikte [OrderMetadataEntry!]! olur — Apollo’nun ve üretimdeki proto-to-GraphQL gateway’lerinin çoğunun kullandığı kalıbın aynısıdır. Tüm dönüşüm istemci tarafında çalışır; şemanızdan hiçbir şey sayfayı terk etmez.
Nasıl kullanılır
Üç adım. Çıktı, SDL kabul eden her GraphQL sunucusuna doğrudan yapıştırılabilir.
.proto şemanızı yapıştırın
Şemayı sol editöre bırakın. En başta yer alan syntax = "proto3"; isteğe bağlı — parser iç içe message blokları, enum bildirimleri, oneof, map<K, V> ve alan opsiyonları ile başa çıkar. Import’lar tanınır ama atlanır, dolayısıyla bağlı olduğunuz tipleri inline olarak yapıştırın.
Alan adı dönüşümü otomatiktir: .proto’daki order_id, GraphQL’de orderId olur. Tip ve enum adları PascalCase olarak kalır. Map alanlarına <Üst><Alan>Entry adında sentetik bir entry tipi eklenir.
Çıktıyı okuyun
Sağda: önce enum’lar, sonra map entry tipleri, sonra bildirim sırasına göre message’lar bulunan GraphQL SDL’i. İç içe tipler ebeveynlerinden önce gelir; böylece dosya yukarıdan aşağıya akıcı şekilde okunur. Sunucunuzun graphql-tools veya buildSchema ile yüklediği bir .graphql dosyasına bırakın.
Resolver’ları bağlayın
Şema şekli verir; resolver’ları yine de yazmanız gerekir (ya da gRPC servisinizden üretirsiniz). Hızlı yol için bir Apollo Server’ı bu SDL’e ve stub resolver’lara yöneltin, ardından her stub’ı gRPC backend çağrısıyla değiştirin. Çalışma zamanı sözleşmeniz proto3 varsayılanlarından farklıysa nullability’yi ayarlayın.
Gerçekten zaman kazandırdığı yerler
Bir gRPC backend üzerine GraphQL gateway kurmak
Ekibinizin bir gRPC servisi var ve ürün, web istemcisi için GraphQL endpoint’i istiyor. Resolver yazmadan önce frontend ekibiyle konuşmak için bir başlangıç şeması gerekiyor. Proto’yu yapıştır, SDL’i kopyala, bir dokümana bırak — bitti.
Bir Protobuf API değişikliğini incelemek
Backend’den bir arkadaş bir message’a alanlar ekledi. Tüm codegen pipeline’ını yeniden çalıştırmadan, bunun genel GraphQL yüzeyini nasıl etkilediğini görmek istiyorsunuz. Yeni .proto’yu yapıştırın, SDL çıktısını mevcut şemanızla diff alın, odaklanmış bir inceleme yorumu bırakın.
Dokümantasyon ve tasarım tartışmaları
İleride önüne GraphQL koyulacak yeni bir gRPC servisi için RFC yazıyorsunuz. Her iki şekli de dokümana koymak — bir tarafta proto, diğer tarafta SDL — konuşmayı somutlaştırır. Bu dönüştürücüyle SDL tarafını build kurmadan elde edersiniz.
REST veya gRPC'den GraphQL'e geçiş
Protobuf ile tanımlanmış bir servisi devraldınız ve yeni ürün brief’i bir GraphQL API’si istiyor. Geçişin başlangıç noktası olarak burada bir taslak şema üretin, sonra alan adları, nullability ve sayfalama üzerinde elle yineleme yapın.
Sık sorulan sorular
Şemam herhangi bir yere gönderiliyor mu?
.proto parser ve SDL emitter, ikisi de tarayıcınızda JavaScript olarak çalışır. DevTools’u açıp Network sekmesini izleyerek yapıştırın — sıfır istek görürsünüz. Şemanızda iç tip adları, paket yolları veya üçüncü taraf bir servise göndermek istemeyeceğiniz başka şeyler varsa bu işe yarar.
64-bit int’ler neden String olarak yazılıyor?
GraphQL’in yerleşik Int skaleri, GraphQL spesifikasyonuna göre 32-bit işaretlidir; proto’nun int64 veya uint64’ünü tutamaz. Geleneksel çözüm, özel bir skaler (genelde BigInt, Long veya Int64 denir) tanımlamak ve değeri string olarak serileştirmektir. Bu dönüştürücü, şema kutudan çıkar çıkmaz geçerli olsun diye tüm 64-bit tamsayı tipleri için String üretir; sunucuda özel skalerinizi tanımladığınızda bu yerleri o adla değiştirirsiniz.
Map alanları nasıl ele alınıyor?
GraphQL’in yerel map tipi yok — { String: String } şeklinde bir biçim mevcut değil. Standart çözüm, map’i bir { key, value } çiftleri listesine açmaktır. Yani message Order içindeki map<string, string> metadata = 8;, sentetik bir type OrderMetadataEntry { key: String! value: String! } ile birlikte metadata: [OrderMetadataEntry!]! olur. Entry tipinin adı, üst message + alan adının PascalCase hâli + Entry şeklinde türetilir; bu, rejoiner dahil çoğu proto-to-GraphQL gateway’inin kullandığı gelenektir.
oneof nasıl ele alınıyor?
Bir oneof’un her alanı, grubu işaretleyen bir yorumla birlikte sıradan bir nullable alan olarak yazılır. GraphQL’de proto’nun oneof’una temiz şekilde eşleşen yerel bir ayrımlı birlik kavramı yok — en yakın karşılığı özel bir union tipi ya da (yakın zamanda spesifikasyona girdi tarafı için eklenen) @oneOf input direktifi. Çıktı tipleri için çoğu şema, oneof’un her durumunu nullable olarak yazıp kısıtı dokümante eder; biz de burada öyle yapıyoruz. Sıkı union tipleri istiyorsanız çıktıyı elle düzenleyin.
Skalerler neden non-null ama mesajlar nullable?
Bu, proto3 semantiğine uyar. proto3’te skalerler telde her zaman bir değer taşır — atanmamış bir string alan varsayılan olarak "", atanmamış bir int32 ise 0 olur. optional ya da wrapper tipler kullanmadan, skalerler için "varsayılana atanmış" ile "atanmamış" arasında ayrım yapmanın yolu yok. Tek değerli iç içe mesajlarsa has-value semantiğine sahiptir — alan etiketi tamamen eksik olabilir — bu yüzden doğal olarak nullable GraphQL alanlarına eşlenir. repeated alanlar her zaman non-null öğelerden oluşan non-null listelerdir, yine proto3’le uyumludur.
google.protobuf.Timestamp ve diğer well-known tipler için ne oluyor?
GraphQL’in yerleşik DateTime skaleri olmadığı için well-known tipler varsayılan olarak String olarak yazılır. Üretim şemalarının çoğu özel bir DateTime (veya ISO8601String) skaleri tanımlar ve üretim sonrası String geçişlerini onunla değiştirir. google.protobuf.Duration, Any ve Value için de aynısı geçerli — önce String olarak yaz, tanımladıktan sonra özel skalerlerle değiştir.
bytes alanları — neden String?
GraphQL’in yerel bir ikili skaleri yok. Geleneksel kodlama, bir String alanın içine base64 yazmaktır; bu aynı zamanda proto3 JSON mapping’inin bytes için kullandığı yöntemdir. Daha sıkı tipleme istiyorsanız sunucuda Base64String adında özel bir skaler tanımlayın ve o alanlardaki String’i değiştirin.
İlgili araçlar
Protobuf, GraphQL ve JSON ile çalışıyorsanız bunlar iyi gider: