GraphQLでの認証
GraphQL API の認証には、personal access token、GitHub App、または OAuth app を使う必要があります。
personal access token
で認証を行う
personal access tokenを使って認証を行うには、「個人用アクセス トークンを管理する」の手順に従ってください。 要求しているデータによって、必要なスコープまたはアクセス許可が決まります。
たとえば、トークンを使ってアクセスできるリポジトリ内のすべての issue を読み取るには、"issues:read" アクセス許可を選びます。
すべてのfine-grained personal access tokenには、パブリック リポジトリへの読み取りアクセスが含まれています。 personal access token (classic)を使ってパブリック リポジトリにアクセスするには、"public_repo" スコープを選びます。
リソースへのアクセスに必要なスコープまたはアクセス許可がトークンに含まれていない場合は、トークンに必要なスコープまたはアクセス許可を示すエラー メッセージが API によって返されます。
GitHub App
による認証
Organization または他のユーザーの代わりに API を使用する場合、GitHub では、GitHub App の使用が推奨されます。 お使いのアプリにアクティビティを属性付けするには、そのアプリをアプリ インストールとして認証します。 ユーザーにアプリ アクティビティを属性付けするには、お使いのアプリをユーザーに代わって認証します。 どちらの場合も、GraphQL API に対する認証に使うことができるトークンを生成します。 詳細については、「GitHub App の登録」および「GitHub アプリでの認証について」を参照してください。
OAuth app による認証
OAuth app から OAuth トークンによる認証を受けるには、まず Web アプリケーション フローまたはデバイス フローのいずれかを使って OAuth app を認可する必要があります。 その後は、受け取ったアクセス トークンを使って API にアクセスできるようになります。 詳細については、「OAuth アプリの作成」および「OAuth アプリの承認」を参照してください。
GraphQLのエンドポイント
REST API には多数のエンドポイントがあります。 GraphQL API では、行う操作にかかわらず、エンドポイントは一定のままです。 GitHub.com の場合、そのエンドポイントは次のとおりです。
https://api.github.com/graphql
GraphQLでの通信
GraphQL の操作は複数行の JSON で構成されるため、GitHub は Explorer を使用して GraphQL 呼び出しを行うことをお勧めします。 curl
や、HTTP を使うその他のライブラリを使うこともできます。
REST では、HTTP 動詞によって実行される操作が決まります。 GraphQL では、クエリまたはミューテーションを実行する場合でも、JSON でエンコードされた本文を提供するので、HTTP 動詞は POST
となります。 例外は、エンドポイントに対する単純な GET
であるイントロスペクション クエリです。 GraphQL と REST について詳しくは、「RESTからGraphQLへの移行」をご覧ください。
curl
コマンドで GraphQL にクエリを実行するには、JSON ペイロードを使用して POST
要求を行います。 ペイロードには、query
という文字列が含まれている必要があります。
curl -H "Authorization: bearer TOKEN" -X POST -d " \
{ \
\"query\": \"query { viewer { login }}\" \
} \
" https://api.github.com/graphql
注: "query"
の文字列値は改行文字をエスケープする必要があります。さもないと、スキーマで正しく解析されません。 POST
の本文には、外側の二重引用符とエスケープされた内側の二重引用符を使用します。
クエリ及びミューテーション操作について
GitHubの GraphQL API で許可される操作の 2 種類は 、クエリ_と_変更です。 GraphQL と REST を比較すると、クエリは GET
要求と同様に動作しますが、ミューテーションは POST
/PATCH
/DELETE
のように動作します。 ミューテーション名によって、実行される変更が決まります。
レート制限について詳しくは、「GraphQL API のレート制限とノード制限」をご覧ください。
クエリとミューテーションは似た形式を持っていますが、重要な違いがあります。
クエリについて
GraphQL クエリからは、指定したデータのみが返されます。 クエリを作成するには、スカラーだけを返すまで、フィールド内フィールドを指定する必要があります (入れ子になったサブフィールド とも呼ばれます)。
クエリは次のように構成されます。
query { JSON-OBJECT-TO-RETURN }
実際の例については、「クエリの例」を参照してください。
ミューテーションについて
ミューテーションを作成するには、3つのことを指定しなければなりません。
- ミューテーション名。 実行したい変更の種類です。
- 入力オブジェクト。 サーバーに送信するデータ。入力フィールド で構成されます。 これはミューテーション名に引数として渡してください。
- ペイロード オブジェクト。 サーバーから返すデータ。戻り値のフィールド で構成されます。 これは、ミューテーション名のボディとして渡してください。
ミューテーションは以下のような構造になります。
mutation { MUTATION-NAME(input: {MUTATION-NAME-INPUT!}) { MUTATION-NAME-PAYLOAD } }
この例の入力オブジェクトは MutationNameInput
、ペイロード オブジェクトは MutationNamePayload
です。
ミューテーション参照では、一覧表示された 入力フィールド が入力オブジェクトとして渡されます。 一覧表示されている 戻り値のフィールド は、ペイロード オブジェクトとして渡されます。
実際の例については、「ミューテーションの例」を参照してください。
変数の使用
変数を使用すると、クエリをより動的かつ強力にすることができ、ミューテーションの入力オブジェクトを渡すときの複雑さを軽減できます。
注: Explorer を使用している場合は、別のクエリ変数ペインに変数を入力し、JSON オブジェクトの前に単語 variables
を含めないでください。
以下は、1つの変数を持つクエリの例です。
query($number_of_repos:Int!) {
viewer {
name
repositories(last: $number_of_repos) {
nodes {
name
}
}
}
}
variables {
"number_of_repos": 3
}
変数を利用するには3つのステップがあります。
-
variables
オブジェクト内の操作の外部で変数を定義します。variables { "number_of_repos": 3 }
オブジェクトは有効なJSONでなければなりません。 この例は単純な
Int
変数型を示していますが、入力オブジェクトなど、より複雑な変数型を定義できます。 ここで複数の変数を定義することもできます。 -
変数を操作に引数として渡します。
query($number_of_repos:Int!){
引数はキーと値のペアで、キーは 名前 (例: ) で
$
始まり、$number_of_repos
値は 型 (例:Int
) です。 型が必要かどうかを示す!
を追加します。 複数の変数を定義した場合は、それらをここで複数の引数として含めてください。 -
変数を操作の中で利用してください。
repositories(last: $number_of_repos) {
この例では、変数を取得するリポジトリ数に置き換えています。 GraphQLは強い型付けを強制するので、ステップ2で型を指定しています。
このプロセスでクエリの引数は動的になります。 これで、variables
オブジェクト内の値を変更し、クエリの残りの部分を同じに保つことができます。
変数を引数として使用すると、クエリを変更せずに variables
オブジェクト内の値を動的に更新できます。
クエリの例
もっと複雑なクエリを見ていき、これらの情報を流れの中で捉えていきましょう。
次のクエリでは、octocat/Hello-World
リポジトリを検索し、最新の 20 個の解決された issue を検索し、各 issue のタイトル、URL、最初の 5 つのラベルを返します。
query {
repository(owner:"octocat", name:"Hello-World") {
issues(last:20, states:CLOSED) {
edges {
node {
title
url
labels(first:5) {
edges {
node {
name
}
}
}
}
}
}
}
}
この構造を1行ずつ見ていきましょう。
-
query {
目的はサーバーからデータを読み取ることであり、変更することではありません。
query
はルート操作です。 (操作を指定しない場合は、query
も既定値になります。) -
repository(owner:"octocat", name:"Hello-World") {
クエリを開始するには、
repository
オブジェクトを検索します。 スキーマの検証により、このオブジェクトにowner
とname
の引数が必要であることがわかります。 -
issues(last:20, states:CLOSED) {
リポジトリ内のすべての issue について説明するために、
issues
オブジェクトを呼び出します。 (1 __ つのクエリをissue
実行repository
することもできますが、返す問題の数を把握し、引数として指定する必要があります)。issues
オブジェクトに関するいくつかの詳細を次に示します。- このドキュメントでは、このオブジェクトに型
IssueConnection
があることがわかります。 - スキーマの検証では、このオブジェクトでは引数として結果の
last
の数またはfirst
の数が必要であることがわかるので、20
を指定します。 - このドキュメントでは、このオブジェクトが
states
引数を受け入れることも示しています。これはOPEN
またはCLOSED
の値を受け入れるIssueState
列挙型です。 解決された issue のみを見つけるには、states
キーにCLOSED
の値を指定します。
- このドキュメントでは、このオブジェクトに型
-
edges {
IssueConnection
型を持っているため、issues
は接続であることがわかります。 個々の問題に関するデータを取得するには、edges
を使用してノードにアクセスする必要があります。 -
node {
ここで、エッジの端にあるノードを取り出します。
IssueConnection
このドキュメントでは、IssueConnection
型の末尾にあるノードがIssue
オブジェクトであることが示されています。 -
Issue
オブジェクトを取得することがわかっているので、ドキュメントを確認し、返すフィールドを指定できます。title url labels(first:5) { edges { node { name } } }
ここで、
Issue
オブジェクトのtitle
、url
、labels
フィールドを指定します。labels
フィールドは型LabelConnection
を持っています。issues
オブジェクトと同様に、labels
は接続であるため、そのエッジを接続されたノード (label
オブジェクト) に移動する必要があります。 ノードでは、返すlabel
オブジェクト フィールドを指定できます。この場合は、name
です。
Octocat のパブリック Hello-World
リポジトリでこのクエリを実行しても、多くのラベルが返されないことに気付くかもしれません。 ラベルを使っている自分自身のリポジトリに対してこれを実行してみれば、違いがわかるでしょう。
ミューテーションの例
ミューテーションでは、まずクエリを実行して見なければ分からない情報が必要になることがよくあります。 この例では2つの操作を示します。
- IssueのIDを取得するクエリ。
- 絵文字のリアクションをそのIssueに追加するミューテーション。
query FindIssueID {
repository(owner:"octocat", name:"Hello-World") {
issue(number:349) {
id
}
}
}
mutation AddReactionToIssue {
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
reaction {
content
}
subject {
id
}
}
}
クエリとミューテーションに名前 (この例では FindIssueID
と AddReactionToIssue
) を付ければ、同じ Explorer ウィンドウに含めることができますが、操作は GraphQL エンドポイントへの個別の呼び出しとして実行されます。 クエリをミューテーションと同時に、あるいはミューテーションとクエリを同時に実行することはできません。
例を見ていきましょう。 このタスクはシンプルに見えます。絵文字のリアクションをIssueに加えるだけです。
それでは、クエリから始めることはどのように知ることができるのでしょうか? この時点ではまだわかりません。
サーバー上のデータを変更したい(絵文字をIssueに添付する)ので、まずは役に立つミューテーションを探してスキーマを検索することから始めます。 参照ドキュメントでは、addReaction
ミューテーションが、「Adds a reaction to a subject.
」という説明とともに示されています。完璧です。
このミューテーションのドキュメントには、3つの入力フィールドがリストアップされています。
clientMutationId
(String
)subjectId
(ID!
)content
(ReactionContent!
)
!
は subjectId
と content
が必須フィールドであることを示します。 必須の content
は意味があります。リアクションを追加するため、使用する絵文字を指定する必要があります。
しかし、なぜ subjectId
が必要なのでしょうか。 これはsubjectId
、リポジトリがどの__ 問題に対応_するかを_特定する唯一の方法であるためです。
このため、この例では ID
を取得するためのクエリから始めています。
クエリを1行ずつ調べていきましょう。
-
query FindIssueID {
ここではクエリを実行し、
FindIssueID
という名前を付けます。 クエリに名前を付けるのはオプションだということに注意してください。ここでは、ミューテーションと同じExplorerウィンドウに置けるように名前を付けています。 -
repository(owner:"octocat", name:"Hello-World") {
リポジトリを指定するには、
repository
オブジェクトに対してクエリを実行し、owner
およびname
引数を渡します。 -
issue(number:349) {
issue
オブジェクトに対してクエリを実行し、number
引数を渡すことによって、対応する issue を指定します。 -
id
ここで、
subjectId
として渡すhttps://github.com/octocat/Hello-World/issues/349
のid
を取得します。
クエリを実行すると、MDU6SXNzdWUyMzEzOTE1NTE=
という id
が取得されます。
注: クエリで返される id
は、ミューテーションで subjectID
として渡す値です。 ドキュメントも、スキーマのイントロスペクションでもこの関係は示されません。このことを理解するには、名前の背景となっている概念を理解しなければなりません。
IDが分かれば、ミューテーションで先に進むことができます。
-
mutation AddReactionToIssue {
ここではミューテーションを実行し、
AddReactionToIssue
という名前を付けます。 クエリと同じように、ミューテーションに名前を付けることはオプションです。ここではクエリと一緒に同じExplorerウィンドウに置けるように名前を付けています。 -
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
この行を調べましょう。
addReaction
はミューテーションの名前です。input
は必要な引数キーです。 ミューテーションではこれは常にinput
になります。{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}
は必要な引数の値です。 これは常に、ミューテーションの入力フィールド (この場合はsubjectId
とcontent
) で構成される 入力オブジェクト (したがって中かっこ) になります。
どの値がcontentとして使われるのかは、どのように分かるのでしょうか?
addReaction
のドキュメントでは、content
フィールドの型はReactionContent
です。これは GitHub issue で特定の絵文字のリアクションのみがサポートされているため列挙型になっています。 リアクションとして使える値は以下のとおりです(いくつかの値は対応する絵文字の名前とは異なっていることに注意してください)。コンテンツ Emoji +1
👍 -1
👎 laugh
😄 confused
😕 heart
❤️ hooray
🎉 rocket
🚀 eyes
👀 -
呼び出しの残りの部分は、ペイロードオブジェクトから構成されます。 ここでは、ミューテーションを行った後にサーバーから返してほしいデータを指定します。 これらの行は、
addReaction
のドキュメントから取得したもので、次の 3 つの可能な戻り値フィールドがあります。clientMutationId
(String
)reaction
(Reaction!
)subject
(Reactable!
)
この例では、2 つの必須フィールド (
reaction
およびsubject
) を返します。両方とも必須サブフィールド (それぞれcontent
およびid
) を持っています。
このミューテーションを実行すると、レスポンスは次のようになります。
{
"data": {
"addReaction": {
"reaction": {
"content": "HOORAY"
},
"subject": {
"id": "MDU6SXNzdWUyMTc5NTQ0OTc="
}
}
}
}
これで完了です。 🎉 の上にカーソルを合わせてユーザー名を探し、issue に対する反応をチェックアウトします。
最後に一つ注意です。インプットオブジェクト中で複数のフィールドを渡す場合、構文が扱いにくくなることがあります。 フィールドを変数に移動すると役立ちます。 以下では、オリジナルのミューテーションを変数を使って書き換えています。
mutation($myVar:AddReactionInput!) {
addReaction(input:$myVar) {
reaction {
content
}
subject {
id
}
}
}
variables {
"myVar": {
"subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
"content":"HOORAY"
}
}
先ほどの例では、content
のフィールド値 (これはミューテーション中で直接使用されます) には HOORAY
の周りに引用符がありませんが、変数で使用される場合には引用符があることがわかります。 これには理由があります。
- ミューテーションで
content
を直接使用する場合、スキーマはその値が文字列ではなく_列挙型_であるReactionContent
型であると想定します。 スキーマ検証は、列挙値の周りにクオートを加えるとエラーを投げます。これはクオートが文字列のために予約されているからです。 content
を変数で使用する場合、variables セクションは有効な JSON である必要があるため、引用符が必要です。 スキーマ検証では、実行中に変数がミューテーションに渡されるときに、ReactionContent
型が正しく解釈されます。
列挙型と文字列型の違いの詳細については、公式の GraphQL 仕様に関するページを参照してください。
参考資料
GraphQL 呼び出しを形成する場合は、さらに 多くの ことができます。 以下は、次に見るべき場所です。