Bu yazıyı okuduğunuza göre siz de benim gibi GraphQL’i merak edip, öğrenmeye çalışıyorsunuz demektir. Bu yazıda Github’ın sunduğu GraphQL API’ı üzerinden hızlı bir tanışma yapmayı planlıyorum. Yapacağım işlemler Github API’ına istekler göndermekten ibaret olacak ama genel olarak GraphQL’i tanımak açısından faydalı olacağını düşünüyorum. Başka bir zamanda da Django ile kendi GraphQL API servisimi oluşturmayı deneyeceğim.
Kısaca bahsetmek gerekirse GraphQL, Facebook tarafından 2012 yılında mobil uygulamalarında veri alışverişini kolaylaştırmak için oluşturulmuş bir “query language”. Yani dil bağımsız olarak kendi graph servislerimizi yazabiliriz. 2015 yılında ise proje açık kaynak olarak paylaşıldı. Şu anda kullananlar arasında Github, Shopify, Coursera, Pinterest var.
Fazla uzatmadan GitHub GraphQL API’yı kullanamaya başlayalım. Giriş yaptıktan sonra ilk örnek sorgumuz bizim için hazırlanmış durumda zaten;
Hello there!
query { viewer { login } }
“Execute Query”, “oynat” simgesine sahip butona, tıkladığım zaman aldığım dönen sonuç şu şekilde;
{ "data": { "viewer": { "login": "yusufkaracin" } } }
Burada yaptığımız basit bir sorgu oluşturmaktı. Bir sorgu oluşturduğumuz zaman objelerin sahip olduğu alanların değerini almış oluyoruz. viewer
giriş yapmış kullanıcıyı temsil ediyor, anlayacağınız gibi…
Bir de sorgumuzu şu şekilde yapalım;
{ viewer { login name bio avatarUrl } }
{ "data": { "viewer": { "login": "yusufkaracin", "name": "Yusuf Karaçin", "bio": null, "avatarUrl": "https://avatars3.githubusercontent.com/u/6485586?v=3" } } }
Sorgu ve dönen sonuç aynı şekilde. GraphQL’in en büyük artılarından birisi de bu. Hangi verilere ihtiyacımız varsa, sadece onları isteyip, sorguyla aynı formatta alıyoruz.
Argüman Gönderme
Bazı alanlar için sorgu yazarken argüman göndermemiz gerekebilir. Argümanları parantezler içinde gönderiyoruz. Bilmemiz gereken en önemli nokta, çift tırnağın zorunlu olması. yani login: "github"
yerine login: 'github'
hata verecektir.
{ repositoryOwner(login: "github") { id url avatarUrl } }
Bazı alanlar için argüman göndermek zorunlu. Mesela repository
için name
ve owner
için argüman geçmek gerek.
{ repository(owner: "django") { id createdAt description } } # HATA { "data": null, "errors": [ { "message": "Field 'repository' is missing required arguments: name", "locations": [ { "line": 2, "column": 3 } ] } ] }
Doğru sorgu için name
ve owner
gerekli;
{ repository(owner: "django", name: "django") { id createdAt description } } # CEVAP { "data": { "repository": { "id": "MDEwOlJlcG9zaXRvcnk0MTY0NDgy", "createdAt": "2012-04-28T02:47:18Z", "description": "The Web framework for perfectionists with deadlines." } } }
Aliases ve Fragments
Alias
Aynı alan için farklı argümanlarla sorgu yapmamız gerekirse, “alias
” kullanmamız gerek. Demek istediğimi daha iyi anlatmak için şu sorguyu çalıştırmayı deneyelim;
{ repository(owner: "django", name: "django") { id createdAt description }, repository(owner: "facebookresearch", name: "ELF") { id createdAt description } } # HATA { "data": null, "errors": [ { "message": "Field 'repository' has an argument conflict: {owner:\"\\\"django\\\"\",name:\"\\\"django\\\"\"} or {owner:\"\\\"ELF\\\"\",name:\"\\\"facebookresearch\\\"\"}?", "locations": [ { "line": 2, "column": 3 }, { "line": 7, "column": 3 } ] } ] }
repository
için farklı argümanlarla sorgu yapmayı denedik ama “argument conflict” hatası aldık. Bu hatanın önüne geçmek için sorgumuzu şu şekilde düzeltebiliriz;
{ djangoQuery: repository(owner: "django", name: "django") { id createdAt description }, elfQuery: repository(owner: "facebookresearch", name: "ELF") { id createdAt description } }
Fragment
Yukarıdaki sorgulardakiid
, createdAt
, description
gibi tekrar tekrar aynı alanların değerini almak istediğimizde, “fragment
” tanımlayıp kullanabiliriz.
fragment repositoryFragment on Repository { id createdAt description } { djangoQuery: repository(owner: "django", name: "django") { ...repositoryFragment } elfQuery: repository(owner: "facebookresearch", name: "ELF") { ...repositoryFragment } }
Connections
Şu ana kadar yaptığımız sorguların sonucunda hiç liste olarak sonuç gelmedi. 1’den çoğa ilişkisi olan bir senaryoda, bir albümün birden fazla şarkıya sahip olması gibi, “connection” devreye giriyor. GitHub GraphQL API üzerinden sahip olduğumuz repoları listeleyen sorguya bakarsak aslında çok basit bir şeyden bahsettiğimi anlarız;
{ repositoryOwner(login: "github") { id repositories(first: 2) { edges { node { id name description } } } } } # CEVAP { "data": { "repositoryOwner": { "id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", "repositories": { "edges": [ { "node": { "id": "MDEwOlJlcG9zaXRvcnkxODQ5OTY=", "name": "version_sorter", "description": "Fast sorting of version numbers" } }, { "node": { "id": "MDEwOlJlcG9zaXRvcnkzNTU4OTM=", "name": "markup", "description": "The code we use to render README.your_favorite_markup" } } ] } } } }
Sorgu sonuçlarının görüntülendiği pencerenin sağ üstündeki “Docs”a tıklayıp, repositories
‘e bakarsak RepositoryConnection
tipinde olduğunu görürüz. Sırayla edges
ve node
alanlarının detayına bakarsak her bir node
aslında Repository
tipinde olduğunu görürüz.
Bu vesileyle de GraphQL ile iç içe sorgular yapmanın aslında ne kadar basit olduğunu görmüş olduk.
Değişken Tanımlama
Şu ana kadar yaptığımız sorgularda değişkenlerden faydalanmadık. GitHub GraphQL API’da sorguları yazdığımız pencerenin hemen altında “Query Variables” isminde başka bir pencere var. Şimdi hem sorgumuza bir isim verelim hem de bu sorgumuza argüman geçerken değişkenleri kullanalım;
# Query Variables { "login": "github", "first": 5 } # Query query GetRepositoriesByLogin($login:String!, $first:Int!){ repositoryOwner(login: $login) { id repositories(first: $first) { edges { node { id name description } } } } }
Mutation
Belki çoktan aklınıza şu soru gelmiştir: “Hep data almak için mi sorgu yapıyoruz? Datalar üzerinde değişikleri nasıl yapabiliriz?”. Sorunun cevabı mutation
“keyword”‘ünde saklı. GitHub GraphQL API kullandığımız için, Github’ın bize sunduğu mutation
larından üzerinden sorgumuzu yapacağız. Yapmaya çalışacağımız şey, yeni yorum eklemek. Önce test için yeni bir repo oluşturalım ve issue ekleyelim.
Şu sorgu ile de açtığımız son repoyu ve “issue”ları listeleyelim. Issueların id
bilgisi(subjectId
) gerekli olacak.
query($login: String!, $last: Int!) { repositoryOwner(login: $login) { id repositories(last: $last) { edges { node { id name description issues(first: 2) { edges { node { id title } } } } } } } }
Şimdi de gelelim mutation
oluşturmaya;
# Query Variables { "input": { "clientMutationId": "123", "subjectId": "MDU6SXNzdWUyNDE0ODM4NzM=", "body": "Bu yorum GraphQL API Explorer ile yapıldı" } } # Query mutation AddComment($input: AddCommentInput!) { addComment(input: $input) { clientMutationId subject { id } } } # CEVAP { "data": { "addComment": { "clientMutationId": "123", "subject": { "id": "MDU6SXNzdWUyNDE0ODM4NzM=" } } } }
Burada subjectId
dışındaki değerleri biz belirledik. “Query Variables” kısmında göndereceğimiz obje için “Docs > addComment > AddCommentInput” bölümüne bakabilirsiniz. Sorgu sonucu dönecek cevap AddCommentPayload
tipinde olacak ve yine “Docs” kısmından hangi alanları kullanabileceğimizi görebilirsiniz.
Bye!
Çok kısa da olsa GraphQL ile tanışmış olduk ve sadece ufak bir kısmını denedik. graphql.org/code adresinde göreceğiz gibi bir çok dilde kendi servisimizi yazmamız mümkün ve GraphQL hakkında öğreneceğimiz çok şey bizi bekliyor.
Yorumlar