はじめに
フィヨルドブートキャンプ(以下、FBC)の「自分で考えたサービスをリリースする」という最終課題で、GitHubのREST APIを利用した。(※近日リリース予定)
外部サービスのAPIをたたくのは今回が初めてで、
- いきなりラッパーライブラリ*1を使ってAPIをたたこうとしたら、使い方が全く分からなかった
- GitHubが用意してくれているSinatraアプリでAPIをたたこうとしたら、認証の話などが出てきて大混乱
という事態に陥った。
API,ラッパーライブラリ,OAuthアプリケーションによる認証など、分からないことが同時に複数出てきて、どのようにサービス開発のための技術検証を進めていけばいいか悩んだので、FBCの卒業生の方々に相談したところ、
「認証の仕組み」と、「APIから欲しい情報を取得できるか」は分けて考えたほうが良い。
認証の理解は難しいので、まず欲しいデータを取れるかどうかを、認証を行うOAuthアプリケーションではなく、ターミナル上でcurlコマンドを使って試すといい。
とアドバイスをいただいた。
なので、「自分が欲しい情報を、そもそもGitHub APIが持っているか」
を検証するために、curl
コマンドで様々なAPIリクエストを試した。
この記事の内容
当時curl
コマンドでAPIをたたいた時につまづいたことを中心に、
- REST と GraphQL の違い・特徴
curl
コマンドを使ったAPIのたたき方- 「GitHubAPIに対してやりたいこと」を実現できるエンドポイントの探し方
- 認証した上でAPIリクエストする方法
- 今自分が許可されているリクエスト回数の上限の確認方法
- クエリパラメーターの使い方
についてまとめた。
目次
- はじめに
- この記事の内容
- 目次
- GitHubのAPIを使うと、どんなことができるのか?
- REST API と GraphQLのちがい
- REST APIに、curlコマンドでリクエストする
- おわりに
- 参考記事
GitHubのAPIを使うと、どんなことができるのか?
- Issueの作成・更新・取得・削除
- Organizationsの一覧やメンバーの取得
- Pull Requestの生成・更新・取得・削除
- Repositoryの生成・更新・取得・削除
などを行うことができる。できることは多岐に渡るので、この他にもたくさんある。
REST API と GraphQLのちがい
GitHubが用意しているAPIには、RESTとGraphQLの2種類がある。
REST
欲しいデータが入っているAPIのエンドポイント*2を指定してデータを得る。
自分が欲しいデータ以外もごっそり入っているので、アプリケーション開発等で特定の情報だけ使いたい場合、コードを書いて欲しい情報だけ得られるように加工する必要がある。
特徴
使用例
例えば、とあるユーザーのGitHubアカウント名とアイコン画像のURLを取得したい場合、以下のようなエンドポイントを指定してデータを得ることになる。(参考:ユーザー - GitHub Docsより)
# Saki-htrというユーザーのpublicな情報を取得する $ curl https://api.github.com/users/Saki-htr { "login": "Saki-htr", # アカウント名 "id": 58052292, "node_id": "MDQ6VXNlcjU4MDUyMjky", "avatar_url": "https://avatars.githubusercontent.com/u/58052292?v=4", # アイコン画像のURL "gravatar_id": "", "url": "https://api.github.com/users/Saki-htr", "html_url": "https://github.com/Saki-htr", "followers_url": "https://api.github.com/users/Saki-htr/followers", "following_url": "https://api.github.com/users/Saki-htr/following{/other_user}", "gists_url": "https://api.github.com/users/Saki-htr/gists{/gist_id}", "starred_url": "https://api.github.com/users/Saki-htr/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/Saki-htr/subscriptions", "organizations_url": "https://api.github.com/users/Saki-htr/orgs", "repos_url": "https://api.github.com/users/Saki-htr/repos", "events_url": "https://api.github.com/users/Saki-htr/events{/privacy}", "received_events_url": "https://api.github.com/users/Saki-htr/received_events", "type": "User", "site_admin": false, "name": "服部紗希", "company": null, "blog": "https://saki-htr.hatenablog.com/", "location": "Tokyo Japan", "email": null, "hireable": null, "bio": null, "twitter_username": null, "public_repos": 33, "public_gists": 1, "followers": 1, "following": 0, "created_at": "2019-11-21T21:35:26Z", "updated_at": "2022-07-25T12:05:10Z", "private_gists": 16, "total_private_repos": 1, "owned_private_repos": 1, "disk_usage": 6885, "collaborators": 0, "two_factor_authentication": true, "plan": { "name": "free", "space": 976562499, "collaborators": 0, "private_repos": 10000 } }
このJSONデータのlogin
キーにアカウント名、avatar_url
キーにアイコン画像のURLが入っている。
このように、REST APIでは、欲しい情報のみを取得することはできない。
GraphQLとは
クライアント側が、必要なデータだけをAPIから取ってこれるように設計された言語。
特徴
- GraphQLは欲しいデータと型をピンポイントで指定して、欲しい情報だけ受け取れる
- クエリの実行:言語で欲しい情報を要求する(クエリを実行する)
- 受け取る方の型を指定できる
- 必要とするデータだけを取得できる
使用例
先程のRESTと同じように、とあるユーザーのGitHubアカウント名とアイコン画像のURLを取得したい場合、以下のようなクエリを書くと、アカウント名とアイコン画像だけが入ったJSONデータを取得できる。
# 実行するクエリ query { user(login: "Saki-htr") { login avatarUrl } }
# レスポンス { "data": { "user": { "login": "Saki-htr", "avatarUrl": "https://avatars.githubusercontent.com/u/58052292?u=655c1af43b9c8331b65387923ef25d6bc197cded&v=4" } } }
このように、欲しい情報のみを取得することができる。
公式が用意してくれている以下のページで、手軽にクエリを実行&レスポンスの確認ができる。
REST APIに、curlコマンドでリクエストする
RESTにリクエストする時の基本
REST API にリクエストを行うときは、HTTP メソッド(例:GET,POST)とパス(URL)を指定する。これに加えて、リクエストヘッダー、パス、クエリ、またはボディのパラメーターを指定することもできる。
以下に実際にcurl
コマンドでたたいた時の例を書いた。
例:指定したリポジトリのissueのリストを取得する
- 指定したリポジトリのissueのリストを取得するには、issuesエンドポイントを使う。
(詳細:issues (List repository issues) - GitHub Docs)
# 書き方 curl https://api.github.com/repos/<リポジトリのオーナーになっているユーザー名>/<リポジトリ名>/issues # 例:fjordllc/bootcampリポジトリのissue一覧を取得する curl https://api.github.com/repos/fjordllc/bootcamp/issues
(返ってくるデータの量が膨大なので、返ってくるJSONデータを見たい方は以下のURLにアクセスをお願いします🙏)
「GitHubAPIに対してやりたいこと」を実現できるエンドポイントの探し方
以下の公式ドキュメントに一覧が載っているので、ここから探す。
ただ、この目次にはHTTPメソッドとパスしか記載されていないため、APIを触り慣れていない当初はここから探すのは難しかった。
「やりたいこと」から探したい場合、各見出しのエンドポイントをクリックすると、実現したい事柄ごとのリストのページにアクセスできる。
例えば、issueに対してやりたいことを実現するURLを探したい場合、まず「issues」という見出しをクリックする。
すると、issue - GitHub Docsというページにとぶ。
そのエンドポイントでできることが目次で載っているので、分かりやすい。
ヘッダー情報も取得したい場合
curl
コマンドで-i
オプションを付けると、ヘッダー情報も含めてレスポンスが返される。
$ curl -i https://api.github.com/users/Saki-htr # レスポンスヘッダー HTTP/2 200 server: GitHub.com date: Sun, 06 Nov 2022 03:15:35 GMT content-type: application/json; charset=utf-8 cache-control: public, max-age=60, s-maxage=60 vary: Accept, Accept-Encoding, Accept, X-Requested-With etag: W/"368a3f29b158b6dcc6f93af02e427ef105cfea0a8dc8eeeed6824f664419d4dc" last-modified: Mon, 25 Jul 2022 12:05:10 GMT x-github-media-type: github.v3; format=json access-control-expose-headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset access-control-allow-origin: * strict-transport-security: max-age=31536000; includeSubdomains; preload x-frame-options: deny x-content-type-options: nosniff x-xss-protection: 0 referrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin content-security-policy: default-src 'none' x-ratelimit-limit: 60 x-ratelimit-remaining: 59 x-ratelimit-reset: 1667708135 x-ratelimit-resource: core x-ratelimit-used: 1 accept-ranges: bytes content-length: 1340 x-github-request-id: C80B:546D:182C4C:1EA570:636726D7 # レスポンスボディ { "login": "Saki-htr", "id": 58052292, "node_id": "MDQ6VXNlcjU4MDUyMjky", "avatar_url": "https://avatars.githubusercontent.com/u/58052292?v=4", "gravatar_id": "", "url": "https://api.github.com/users/Saki-htr", "html_url": "https://github.com/Saki-htr", "followers_url": "https://api.github.com/users/Saki-htr/followers", "following_url": "https://api.github.com/users/Saki-htr/following{/other_user}", "gists_url": "https://api.github.com/users/Saki-htr/gists{/gist_id}", "starred_url": "https://api.github.com/users/Saki-htr/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/Saki-htr/subscriptions", "organizations_url": "https://api.github.com/users/Saki-htr/orgs", "repos_url": "https://api.github.com/users/Saki-htr/repos", "events_url": "https://api.github.com/users/Saki-htr/events{/privacy}", "received_events_url": "https://api.github.com/users/Saki-htr/received_events", "type": "User", "site_admin": false, "name": "服部紗希", "company": null, "blog": "https://saki-htr.hatenablog.com/", "location": "Tokyo Japan", "email": null, "hireable": null, "bio": null, "twitter_username": null, "public_repos": 33, "public_gists": 1, "followers": 1, "following": 0, "created_at": "2019-11-21T21:35:26Z", "updated_at": "2022-07-25T12:05:10Z" }
REST APIのリクエスト制限
リクエスト回数には制限があるので注意
REST APIには、リクエスト回数に制限がある。条件によって、どのような制限がかかるか異なる。
詳細は公式ドキュメントのREST API のリソース - GitHub Docsで説明されている。
これまでの上述の例のように、curl
コマンドで未認証でREST APIにリクエストする場合、1時間あたり最大60件しかリクエストが許可されていない。この上限を超えるとAPIにリクエストできなくなり、たたいても以下のレスポンスが返ってくるようになる。
{ "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", "documentation_url": "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting" }
現在許可されているリクエストの最大回数,残りの回数を確認する方法
-i
オプションをつけた時に返ってくるヘッダーの中にリクエスト制限に関する情報がのっている。
HTTP/2 200 (...省略...) # 1時間あたりに許可されるリクエストの最大数 x-ratelimit-limit: 60 # 現在のレート制限ウィンドウに残っているリクエストの数 x-ratelimit-remaining: 58 # 現在のレート制限ウィンドウがリセットされる時刻 (UTCエポック秒) x-ratelimit-reset: 1667708135 # 現在のレート制限ウィンドウで行ったリクエストの数 x-ratelimit-used: 2 (...省略...)
1時間あたりのリクエスト回数を上げるには、認証が必要
1時間あたりのリクエスト回数を上げるには、GitHub API で認証を行う必要がある。
(公式:REST API の使用を開始する - GitHub Docsより)
ターミナルでcurl
コマンドを使ってAPIリクエストする場合
認証方法は複数あるが、私は、公式ドキュメントで一番簡単な方法と紹介されていた、パーソナルアクセストークンによる認証を使った。
1. パーソナルアクセストークンの作成
https://github.com にログインし、https://github.com/settings/tokens にアクセスし、パーソナルアクセストークンを作成する。
具体的な手順は以下のドキュメントに載っている
2. 作成したトークンを環境変数として保存する
取得したトークンを直打ちしても認証することはできる。 が、公式ドキュメントに
これらの値は、GitHub や、その他あらゆるパブリックな場所には、決して保存しないでください。それらを 環境変数として保存することをお勧めします。 トークン用の変数を設定することで、シェルの履歴にトークンが残るのを避けることができます。
(認証の基本 - GitHub Docsより)
とあるので、環境変数として保存した。
私の場合はfishシェルを使っているので、以下のように実行することで、環境変数を設定した。
$ set <任意の環境変数名> <作成したパーソナルアクセストークン> # 例 $ set API_TOKEN <作成したパーソナルアクセストークン>
echo
コマンドで出力して設定できていることを確認する。※環境変数名の前に$
を付けるのを忘れない。
$ echo $API_TOKEN #=><作成したパーソナルアクセストークン>
3. -u
オプションを使って、認証した上でAPIリクエストを実行する
curl
コマンドの-u
オプションを使って、ユーザー名とパーソナルアクセストークンを送信することで、GitHubAPIに認証をした上でAPIリクエストを実行できる。
$ curl -u <自分のGitHubアカウントのユーザー名>:<パーソナルアクセストークンを設定した環境変数名> https://api.github.com/<リクエストしたいパス> # 例 $ curl -u Saki-htr:$API_TOKEN https://api.github.com/repos/fjordllc/bootcamp/issues
-i
オプションも合わせて使い、ヘッダーを確認することで、リクエスト回数の上限が増えたことが確認できる。
$ curl -u Saki-htr:$API_TOKEN https://api.github.com/repos/fjordllc/bootcamp/issues (...省略...) # 1時間あたりに許可されるリクエストの最大数 x-ratelimit-limit: 5000 ✅60→5000に増えている! # 現在のレート制限ウィンドウに残っているリクエストの数 x-ratelimit-remaining: 4997 # 現在のレート制限ウィンドウがリセットされる時刻 (UTCエポック秒) x-ratelimit-reset: 1667710840 # 現在のレート制限ウィンドウで行ったリクエストの数 x-ratelimit-used: 3 (...省略...)
アプリケーションからAPIリクエストする場合
自分のPCからではなく、自分が開発しているアプリケーションから認証させてAPIリクエストを行いたい場合は、パーソナルアクセストークンではなく、APIリクエストさせたいアプリケーションをOAuthアプリとして登録する必要がある。
OAuthアプリの登録方法は以下のドキュメントに詳しく載っている。
クエリパラメーターの使い方
各エンドポイントには、そのエンドポイントで使えるクエリパラメーターがある。何が使えるかは、公式ドキュメントの各エンドポイントのドキュメントに載っている。
以下は、Pulls (List pull requests)- GitHub Docsのドキュメント。
このエンドポイントにAPIリクエストすると、指定したリポジトリのPullRequestの一覧が取得できるが、もっと条件をしぼって取得したいとする。
そのような場合、用意されているクエリパラメーターを使って以下のように、取得するPullRequestを絞ることができる。
# stateがclosed状態になっているPR $ curl "https://api.github.com/repos/fjordllc/bootcamp/pulls?state=closed" # 更新された順にPRが返ってくる $ curl "https://api.github.com/repos/fjordllc/bootcamp/pulls?sort=updated" # 検索にヒットした最初の3件だけ取得する $ curl "https://api.github.com/repos/fjordllc/bootcamp/pulls?per_page=3"
クエリパラメーターを使う時は、URL全体を""
で囲まないと、以下のようにエラーになってしまうので、注意。
$ curl https://api.github.com/repos/fjordllc/bootcamp/pulls?state=closed fish: No matches for wildcard 'https://api.github.com/repos/fjordllc/bootcamp/pulls?state=closed'. See `help wildcards-globbing`. curl https://api.github.com/repos/fjordllc/bootcamp/pulls?state=closed ^
複数使いたい場合は、&
で繋ぐ。
# updated順に並び替えをして、最初の3つのPRを取得する $ curl "https://api.github.com/repos/fjordllc/bootcamp/pulls?sort=updated&per_page=3"
クエリパラメーターを使っていない時の挙動は、どこを見たら分かるのか?
そもそもクエリパラメーターを使わないでAPIリクエストした時の挙動がどうなっているかは、公式ドキュメントの各クエリパラメーターのDefault:
を見ると分かる。
今回の場合、Defaultは
state
:open
sort
:created
per_page
:30page
:1
とあるので、https://api.github.com/repos/fjordllc/bootcamp/pulls
にリクエストすると、
- openになっているPR かつ
- 作成された順に並び替え かつ
- 1ページあたりの検索結果数が30件 かつ
- 1ページ目
つまり、openな状態のPRを、作成された順に検索してヒットした最初の30件が返ってくることになる。
1回のリクエストでもっと多くの数を取得したい
なので、1回のリクエストでもっと多くの数を取得したい場合、per_page
で件数を指定する必要がある。最大100件まで指定することができる。
per_page と page とは?
per_page と pageが少し分かりにくいので補足する。
例えば、指定したリポジトリのPRのうち、openなものが100件あるとする。
この時、
$ curl https://api.github.com/repos/fjordllc/bootcamp/pulls?state=open&per_page=40
とリクエストすると、以下のような構成になる。
- page1: 最初の40件
- page2: その次の40件
- page3: その次の20件
ここで、page2の部分のPR40件を取得したい場合は、
curl https://api.github.com/repos/fjordllc/bootcamp/pulls?state=open&per_page=40&page=2
というようにpage
を指定する。
おわりに
今は欲しい情報を取得できるエンドポイントをささっと調べることができるが、APIを初めてたたいた当初は、公式ドキュメントを見てもリクエストのやり方が全然分からなかったので、成長を感じて嬉しい😁