İç İçe Mutationların Açıklaması
Mutations, GraphQL sunucusundaki verileri değiştirebilen işlemlerdir; örneğin bir gönderi oluşturma, kullanıcı adını güncelleme, gönderiye yorum ekleme gibi işlemler bu kapsamda yer alır.
GraphQL'de mutations yalnızca MutationRoot türü altında açığa çıkar:
type MutationRoot {
createPost(id: ID!, title: String!, content: String): Post!
updateUserName(userID: ID!, newName: String!): User!
addCommentToPost(postID: ID!, comment: String!, userID: ID): Comment!
}(Bu kılavuzdaki GraphQL şeması örnekleri göstermek amacıyla kullanılmaktadır; eklentinin sunduğu şemadan farklıdır.)
Bu şemayla kullanıcı adını değiştirmek şu şekilde yapılır:
mutation {
updateUserName(userID: 37, newName: "Peter") {
name
}
}Mutations, GraphQL spesifikasyonunda açıklandığı üzere seri olarak yürütülmelerini sağlamak amacıyla yalnızca mutation root nesne türünde açığa çıkarılır:
It is expected that the top level fields in a mutation operation perform side‐effects on the underlying data system. Serial execution of the provided mutations ensures against race conditions during these side‐effects.
"Seri yürütme" terimi, alanları çözmek için önerilen davranış olan "paralel yürütme" ile karşıttır.
Örneğin aşağıdaki sorguda, GraphQL sunucusunun hangi alanı (name mi email mi) önce çözdüğü önemli değildir ve bu alanlar paralel olarak çözülebilir:
query {
user(by: { id: 37 }) {
name
email
}
}Mutations ise veriyi değiştirdiğinden alanların hangi sırayla çözüldüğü önemlidir; bu nedenle seri olarak yürütülmeleri gerekir (aksi takdirde yarış koşullarına yol açabilirler).
Örneğin aşağıdaki iki sorgu farklı sonuçlar üretecektir:
# Query 1: yürütme sonrasında kullanıcı adı "John" olacak
mutation {
updateUserName(userID: 37, newName: "Peter") {
name
}
updateUserName(userID: 37, newName: "John") {
name
}
}
# Query 2: yürütme sonrasında kullanıcı adı "Peter" olacak
mutation {
updateUserName(userID: 37, newName: "John") {
name
}
updateUserName(userID: 37, newName: "Peter") {
name
}
}Mutations'ın yalnızca MutationRoot aracılığıyla açığa çıkarılmasının sonucu, bu türün seri yürütülmek zorunda olmak (ki bu teknik bir gereklilik olup bir arayüz tasarım kararı değildir) dışında aralarında hiçbir ortak nokta bulunmayan alanlarla dolup taşmasıdır.
İç İçe Mutations İçin Gerekçe
Yukarıdaki mutations arasında yalnızca createPost gerçek anlamda MutationRoot türü altında yer alır; çünkü hiçbir şeyden yeni bir öğe oluşturmaktadır. Oysa updateUserName ve addCommentToPost mutations, başka bir türden mevcut bir varlık üzerinde eşdeğer işlemler olarak rahatlıkla uygulanabilir:
type User {
updateName(newName: String!): User!
}
type Post {
addComment(comment: String!, userID: ID): Comment!
}Bu şemayla kullanıcı adını değiştirmek şu şekilde yapılabilir:
mutation {
user(ID: 37) {
updateName(newName: "Peter") {
name
}
}
}Bu özelliğe "nested mutations" adı verilir: bir sorgu veya mutation sonucuna başka bir mutation uygulamak.
İç içe mutations kullanımının GraphQL şemasını nasıl daha zarif hale getirdiğine dikkat edin:
MutationRoot.updateUserNameişlemi kullanıcınınID'sini almak zorundayken, eşdeğerUser.updateNameişlemi buna gerek duymaz; çünkü zaten bir kullanıcı varlığı üzerinde yürütülmektedir- Alan adı
updateUserName'denupdateName'e kısalır
Bunun yanı sıra GraphQL servisi, grafikteki varlıklar arasında gezerek verilerini sorgulayabildiğimiz gibi değiştirebildiğimizden daha sade ve anlaşılır hale gelir.
İç içe mutations birden fazla seviyeye inebilir. Örneğin yeni oluşturulan bir gönderiye tek bir sorgu içinde yorum ekleyebiliriz:
mutation {
createPost(ID: 37, title: "Hello world!", content: "Just another post") {
id
addComment(comment: "Lovely post") {
id
}
}
}Bu sayede iç içe mutations, birden fazla öğeyi değiştirmek için birden fazla sorgu çalıştırmak yerine tek bir sorgu çalıştırarak gidiş-dönüş gecikmesini azaltarak performansı da artırabilir.
Nested Mutations'ın Spesifikasyona Dahil Edilmemesinin Nedeni
GraphQL spesifikasyonu, herhangi bir dil için GraphQL sunucusunun tüm uygulamalarında çalışacak şekilde tasarlanmıştır. Ancak itici gücü, referans uygulama olan graphql-js aracılığıyla JavaScript'tir.
Başka bir deyişle, graphql-js tarafından desteklenemeyen herhangi bir özellik spesifikasyonun parçası olmayacaktır.
JavaScript promises'i desteklediğinden, alanların paralel çözümü mümkün hale gelmiş ve paralellik, toplu işleme fonksiyonları JavaScript promises döndüren DataLoader'da (veri getirme katmanı) da görüldüğü üzere graphql-js'nin ilk tasarımında temel ilkelerden biri haline gelmiştir.
Paralel yürütmenin performans açısından avantajları çok fazladır; oysa iç içe mutations paralelizmle birlikte çalışamaz. Paralel yürütmeyi iç içe mutations ile değiştirmenin buna değmeyeceğine karar verilmiştir.
İç İçe Mutations ve Performans
Gato GraphQL eklentisinde alanlar her zaman seri olarak çözülür ve çözüldükleri sıra deterministiktir. (Bu özellik, sorgu çözümleme performansını etkilemez; çünkü sunucu önce sorgudaki grafiği optimal doğrusal sürede çözülen bir bileşen modeline dönüştürür.)
Bu da eklentinin iç içe mutations'ı destekleyebildiği, tüm avantajlarından yararlandığı ve hiçbir olumsuz sonucuna maruz kalmadığı anlamına gelir.
GraphQL Spesifikasyonu
Bu işlevsellik şu anda GraphQL spesifikasyonunun bir parçası değildir; ancak şu konuda talep edilmiştir: