GraphQL Sorgu Doğrulama (validation)

Harun Akgün
4 min readOct 22, 2018

--

Önceki Sayfa > Şemalar ve Tipler

Tip sistemini kullanarak bir GraphQL sorgusunun geçerli olup olmadığı sorgu çalıştırılmadan önce anlaşılabilir. Bu durum sunucu ve istemcilerin geliştiricileri geçerli olmayan bir sorgu ile ilgili bilgilendirmesini sağlar ve çalışma zamanında herhangi bir kontrole ihtiyaç kalmaz.

Star Wars örneğimizde starWarsValidation-test.js dosyası içerisinde geçersiz çeşitli sorguları gösteren örnekler bulunmaktadır ve bu test dosyası referans implementasyonunun doğrulama sistemi içerisinde çeşitli denemeler yapmanıza izin verir.

Başlamak için karmaşık ve geçerli bir sorguyu ele alalım. Daha önceki örneklerimizde gördüğümüz gibi paylaşılan alanları fragmentler içerisine alınmış iç içe bir sorgu:

sorgu:

{
hero {
...NameAndAppearances
friends {
...NameAndAppearances
friends {
...NameAndAppearances
}
}
}
}
fragment NameAndAppearances on Character {
name
appearsIn
}

cevap:

{
"data": {
"hero": {
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Luke Skywalker",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Han Solo",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
},
{
"name": "Leia Organa",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
},
{
"name": "C-3PO",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
},
{
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
}
]
},
{
"name": "Han Solo",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Luke Skywalker",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
},
{
"name": "Leia Organa",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
},
{
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
}
]
},
{
"name": "Leia Organa",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
],
"friends": [
{
"name": "Luke Skywalker",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
},
{
"name": "Han Solo",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
},
{
"name": "C-3PO",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
},
{
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
}
]
}
]
}
}
}

Daha önce de söylediğimiz gibi bu geçerli bir sorgu. Şimdi de geçersiz birkaç sorguya göz atalım…

Bir fragment kendi kendini içeremez, çünkü bu durum bir döngü yaratacaktır! Aşağıda aynı sorgunun belirtilmiş üç seviyelik katmanlanması olmayan halini görebilirsiniz:

sorgu:

{
hero {
...NameAndAppearancesAndFriends
}
}
fragment NameAndAppearancesAndFriends on Character {
name
appearsIn
friends {
...NameAndAppearancesAndFriends
}
}

cevap:

{
"errors": [
{
"message": "Cannot spread fragment \"NameAndAppearancesAndFriends\" within itself.",
"locations": [
{
"line": 11,
"column": 5
}
]
}
]
}

Alanlar için sorgu yaptığımızda belirli bir tip içerisinde bulunan alanları sorgulamalıyız. Yani hero tipi Character döndüğünden Character içindeki alanları sorgulamak zorundayız. Bu tip favoriteSpaceship adında bir alan içermediğinden aşağıdaki sorgu geçersizdir:

sorgu:

{
hero {
favoriteSpaceship
}
}

cevap:

{
"errors": [
{
"message": "Cannot query field \"favoriteSpaceship\" on type \"Character\".",
"locations": [
{
"line": 4,
"column": 5
}
]
}
]
}

Skalar yada enum tipi haricinde bir tip dönen alanlarda sorgulama yaptığımızda bu alandan hangi veriyi almak istediğimizi de belirtmek zorundayız. Aşağıdaki gibi, veri alanlarını belirtmezsek sorgu geçersiz olacaktır:

sorgu:

{
hero
}

cevap:

{
"errors": [
{
"message": "Field \"hero\" of type \"Character\" must have a selection of subfields. Did you mean \"hero { ... }\"?",
"locations": [
{
"line": 3,
"column": 3
}
]
}
]
}

Benzer şekilde, bir alan skalar tipindeyse, bu alandan alt alanlar beklemek mantıksız olacaktır. Aşağıdaki sorgu bu sebeple geçersizdir:

sorgu:

{
hero {
name {
firstCharacterOfName
}
}
}

cevap:

{
"errors": [
{
"message": "Field \"name\" must not have a selection since type \"String!\" has no subfields.",
"locations": [
{
"line": 4,
"column": 10
}
]
}
]
}

Daha önce belirttiğimiz gibi bir sorgu sadece bahsi geçen tipteki alanları sorgulayabilir. Character döndüren hero sorgusu yaptığımızda sadece Character içerisindeki alanları sorgulayabiliriz. Peki R2-D2'nin ana fonksiyonunu (primaryFunction) sorguladığımızda ne olur?

sorgu:

{
hero {
name
primaryFunction
}
}

cevap:

{
"errors": [
{
"message": "Cannot query field \"primaryFunction\" on type \"Character\". Did you mean to use an inline fragment on \"Droid\"?",
"locations": [
{
"line": 5,
"column": 5
}
]
}
]
}

Bu sorgu geçersizdir, çünkü primaryFunction Character üzerinde bulunan bir alan değildir. Eğer karakter Droid ise primaryFunction alanını çekmek değilse bu alan ile ilgili sorguyu atlamak istediğimizi belirten bir yola ihtiyacımız var. Bunu yapabilmek için daha önce bahsettiğimiz fragment yapısını kullanabiliriz. Droid içerisinde bir fragment tanımı yapıp bu fragment’ı sorguya ekleyerek sadece tanımlandığında primaryFunction alanının cevap olarak dönmesini sağlayabiliriz.

sorgu:

{
hero {
name
...DroidFields
}
}
fragment DroidFields on Droid {
primaryFunction
}

cevap:

{
"data": {
"hero": {
"name": "R2-D2",
"primaryFunction": "Astromech"
}
}
}

Bu sorgu geçerli, ancak biraz fazla uzun. İsimlendirilmiş fragmentlar birden fazla kez kullanılacaksa işimize yarar, ancak yukarıdaki gibi sadece bir kez kullanılacaklarsa gereksizdirler. İsimlendirilmiş fragment kullanmak yerine satıriçi fragment kullanabiliriz. Bu durum ayrı bir fragment isimlendirmeden istediğimiz tipi almamızı mümkün kılar.

sorgu:

{
hero {
name
... on Droid {
primaryFunction
}
}
}

cevap:

{
"data": {
"hero": {
"name": "R2-D2",
"primaryFunction": "Astromech"
}
}
}

Şu ana kadar doğrulama sistemi ile ilgili yüzeysel bir bilgi vermiş olduk. GraphQL sorgularının semantik olarak geçerli olduğunu doğrulayan başka doğrulama kuralları da var. Spesifikasyon dökümantasyonu “Doğrulama” bölümü bu konu ile ilgili daha fazla bilgi veriyor aynı zamanda “Validasyon” klasörü spesifikasyona uyumlu GraphQL doğrulayıcısı implementasyonu ile ilgili kodları içeriyor.

Okumaya Devam Et> Sorgu Çalıştırma

--

--