はじまり
あぁぁ、目が回りそうだぁ!
GraphQLを組み立てているね
これって、()なの? {}なの?
おさらいしてみましょうか
GraphQLを触ってみました
なので、今回はGraphQLに関する記事を書いてみます。
本記事では、GraphQLの基礎的な書き方をから、初心者でも迷わずにクエリを構築できるように、基本的なQueryとMutationの書き方を紹介します。
後の方では、実際にAniListというサービスで叩いた結果を紹介します。
GraphQLの基本的な部分
まず、GraphQLとは?
スゴイざっくり言うと、「APIにアクセスするための仕組み」です。
LinuxでおなじみのRed Hatさんの記事ではこう書いてあります。(会社の業務で触ったLinuxなつかしい・・・)
GraphQL (グラフキューエル、グラフQL) とは、API (アプリケーション・プログラミング・インタフェース向けのクエリ言語とサーバーサイドのランタイムの両方を指します。GraphQL は、クライアントがリクエストしたデータのみを返すことを優先します。
ここから引用
なるほど、クエリだけじゃなくてランタイムも引っくるめて「GraphQL」なんだな。
GraphQLは、同じAPIの仕組みの一つである「Rest API」と比べて、としては、データの取得や更新が行いやすいクエリ言語を備えています。
クエリを書く前に
クエリを書く前に、以下の2点を確認する必要があります。
1. GraphQLスキーマ
GraphQLスキーマは、GraphQLサーバーで提供されているデータの種類と構造を定義するものです。スキーマは、SDL(Schema Definition Language)と呼ばれる言語で記述されます。
2. 取得したいデータ
どのようなデータを取得したいかを明確にしましょう。具体的な項目名や関連データなども考慮する必要があります。
クエリの書き方
基本構文
GraphQLクエリの基本構文は次のとおりです。
query {
# 取得したいデータのフィールドを記述
}
例えば、ユーザー情報と投稿情報を取得するクエリは次のようになります。
query {
user {
id
name
birthDate
}
posts {
id
title
content
}
}
このクエリは、user
フィールドとposts
フィールドを指定しています。user
フィールドではIDとユーザー名を取得し、posts
フィールドではID、題名、内容を取得します。
Arguments
取得する情報に条件を付けることが出来ます。
query {
users(name: "Tom") {
id
name
age
birthDate
}
}
この場合は、名前が「Tom」だけのユーザーを取得することになります。
Nest
ときどき、取得する情報が細分化されて、さらに属性を持っていたりもします。
query {
users(name: "Tom") {
id
name
age
birthDate {
year
month
day
}
}
}
この場合は、ユーザーの誕生日が、さらに年、月、日に分かれているパターンとなります。
Variables
そして、属性のパラメータ値は、まとめて別の場所で宣言することが出来ます。
例えば、こんなクエリに対して、
query {
users(name: "Tom") {
id
name
age
birthDate(year: 2023) {
year
month
day
}
}
}
query
フィールドとは別に、variables
フィールドとして、パラメータを一括に宣言できます。
宣言方法は次の節で・・・
{
userName: "Tom"
birthYear: 2023
}
query ($username: String, $birthYear: Int) {
users(name: $userName) {
id
name
age
birthDate(year: $birthYear) {
year
month
day
}
}
}
パラメータに使える型は、String、Int、Float、Booleanなど、色々あります。
JavaScriptとかでJSON.stringifyすれば・・・
JavaScritpなどでクエリを作る際に、JSON.stringify()を使えば、クエリとは別にvariables
を宣言できます。
例えば、JavaScriptライクな言語「Google Apps Script」からGraphQLを打つ場合は、こんな感じでAPIにアクセスする記述が出来ます。
let variables = {
userName: "Tom"
birthYear: 2023
}
let query = `query ($username: String, $birthYear: Int) {
users(name: $userName) {
id
name
age
birthDate(year: $birthYear) {
year
month
day
}
}
}
`;
let options = {
method: "POST"
, headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}, payload: JSON.stringify({
query: query
, variables: variables
})
};
let response = UrlFetchApp.fetch(url, options);
Mutation
さて、query
フィールドでは、データを参照するのが主な用途ですが、データを書き換えたりもしたくなります。
そこで使うのが、mutation
フィールドです。
mutation {
createUser(input: { name: "Mary", age: 94 }) {
id
name
age
}
}
mutation
フィールドでは、編集する属性を指定して、どの属性をレスポンスとして受け取るかを指定することが出来ます。上記のクエリの場合だと、IDがアッチ側で自動採番だとしてこんな風に返ってきます。
{"data":{"createUser":{"id":3,"name":"Mary,"age":94}}}
Mutationでvariables
mutation
フィールドでも、もちろんvariables
が利用できます。
宣言方法は、「JavaScriptとかでJSON.stringifyすれば・・・」の節と同様。
variables = {
userName: "Mary"
age: 94
}
mutation ($userName: String, $age: Int) {
createUser(input: { name: $userName, age: $age }) {
id
name
age
}
}
Mutation内のNestにvariablesを使う
mutation
フィールド内で入れ子になっている部分にvariables
を使いたい場合に、こんがらがってしまうかもしれません。(実際に僕はこんがらがりました。)
こんな風にします。
variables = {
userName: "Mary"
age: 94
birthYear: 2005
}
mutation ($userName: String, $age: Int) {
createUser(input: { name: $userName, age: $age, birthDate: {year: $birthYear}) {
id
name
age
birthDate {
year
month
day
}
}
}
あくまで、createUser
の「{}」の中身は「参照」したい属性なので、「()」の引数に変数を「設定」しましょう。
variables = {
userName: "Mary"
age: 94
birthYear: 2005
}
mutation ($userName: String, $age: Int) { // <- 変数を設定したい
createUser(input: { name: $userName, age: $age, birthDate: {year: $birthYear}) { // <- 対象を設定したい
id // <- 参照したい
name // <- 参照したい
age // <- 参照したい
birthDate {
year // <- 参照したい
month // <- 参照したい
day // <- 参照したい
}
}
}
そして、実際にAniList APIで叩いてみた
「AniList」というのは、自分が今までに見たアニメや、これから見たいアニメ、読みたいマンガなどを登録、管理できるサービスです。
APIのリファレンスのGetting-Startedはここで、GraphQLの属性のリファレンスはここです。
その管理しているデータベースには、「AniList API」を使って参照することができて、編集することも出来ます。
こんな風に打ち込みます。まずはquery
でデータ取得です。
variables = {
username: "anilistUserName"
, userid: "anilistUserId"
}
query ($username: String, $id: Int) {
MediaListCollection(userName: $username, userId: $id, type: ANIME) {
lists {
entries {
media {
id
title {
native
}
coverImage {
extraLarge
}
siteUrl
studios (isMain: true, sort: FAVOURITES) {
nodes {
name
}
}
}
score (format: POINT_100)
status
progress
completedAt {
year
month
day
}
notes
updatedAt
}
}
}
}
次で、mutation
でデータの編集をしています。
variables = {
mediaId: 21127
, status: "CURRENT"
, score: 100
, progress: 13
, completedAtYear: 2022
, completedAtMonth: 1
, completedAtDay: 13
, notes: "エル・プサイ・コングルゥ。"
}
mutation ($mediaId: Int, $status: MediaListStatus, $score: Float, $progress: Int, $completedAtYear: Int, $completedAtMonth: Int, $completedAtDay: Int, $notes: String) {
SaveMediaListEntry (mediaId: $mediaId, status: $status, score: $score, progress: $progress, notes: $notes, completedAt: {year: $completedAtYear, month: $completedAtMonth, day: $completedAtDay}) {
id
mediaId
status
score (format: POINT_100)
progress
completedAt {
year
month
day
}
notes
}
}
レスポンス
{"data":{"SaveMediaListEntry":{"id":246757832,"mediaId":21127,"status":"CURRENT","score":100,"progress":13,"completedAt":{"year":2022,"month":1,"day":13},"notes":"\u30a8\u30eb\u30fb\u30d7\u30b5\u30a4\u30fb\u30b3\u30f3\u30b0\u30eb\u30a5\u3002"}}}
複数レコードを編集したいときは?
AniList APIでは、「1分間に90リクエストまで」のレート制限が設けられています。
そのため、複数のアニメを編集したいときは、クエリをまとめて送りたいですよね。
Google Apps Scriptで組むと、こんな風に複数レコードに対する1クエリが作成できます。
let variableses = [
{
"mediaId": 21127
, "status": "CURRENT"
, "score": 100
, "progress": 13
, "completedAtYear": 2021
, "completedAtMonth": 1
, "completedAtDay": 13
, "notes": "エル・プサイ・コングルゥ。"
}
, {
"mediaId": 9253
, "status": "PLANNING"
, "score": 90
, "progress": 13
, "completedAtYear": 2017
, "completedAtMonth": 3
, "completedAtDay": 15
, "notes": "ファーーーハッハッハッハ"
}
];
console.log(variableses.length);
console.log(`getRequestOptionsAnilist: 3333333333333333333333333333333333333333333333`);
for(let i = 0; i < variableses.length; i++){
query = `
${query}
saveMedia${i}: SaveMediaListEntry (mediaId: ${variableses[i]["mediaId"]}, status: ${variableses[i]["status"]}, score: ${variableses[i]["score"]}, progress: ${variableses[i]["progress"]}, notes: "${variableses[i]["notes"]}", completedAt: {year: ${variableses[i]["completedAtYear"]}, month: ${variableses[i]["completedAtMonth"]}, day: ${variableses[i]["completedAtDay"]}}) {
id
mediaId
status
score (format: POINT_100)
progress
completedAt {
year
month
day
}
notes
}
`;
}
query = `mutation {${query}}`;
console.log(query);
console.log(`getRequestOptionsAnilist: 44444444444444444444444444444444444444444444`);
options = {
method: "POST",
headers: {
"Authorization": `Bearer ${access_token}`,
"Content-Type": `application/json`,
"Accept": `application/json`,
}
, payload: JSON.stringify({
query: query
})
, muteHttpExceptions : true
}
上記でミソなのは、SaveMediaListEntry
の前に、saveMedia${i}:
という風にフィールドに名前を付けている点です。
少し脱線しますが、1つのリクエストに付きのクエリの長さのレート制限は、確認することが出来ませんでした。まあ程々にでしょうか。
まとめ
以上がGraphQLの基礎的なクエリの書き方と少し応用といった感じでした。
query
とmutation
を使いこなして、効果的にデータを取得・変更できるようになると、GraphQLの真価がより体感できるでしょう。
ぜひこれを参考に、日々の活動でGraphQLを活用してみてください。
おしまい
ほぁぁ、こう書くのか!
一回書けてしまえばなぁ。
以上になります!
コメント