
Octokit でリポジトリ一覧を取得してみる
ポートフォリオサイトらしく、なんか作ったものとかも載せたいよね(一応GitHubリンクはおいてたけども)ってことで、今回はサイトに過去に自分がつくったもののリストを載せるべく、GitHub 上にあるリポジトリリストを生成する仕組みを作りました。
Octokit
GitHub はリポジトリの情報を取得するための API が公開されています。
そして、JavaScript をはじめとした様々な言語でこれを簡単に利用できるようにするための、公式 SDK が用意されています。それが、 Octokit です。
今回はこの Octokit を使って自分の GitHub アカウントから、リポジトリの一覧を取得して projects ページを自動生成してみます。
実際に作ってみる
ということで、実際に作っていきます。まずは、必要なライブラリの追加と、アクセストークンの発行を行います。
今回は、Next.js のサイトに載せるので、JS版になります。npm ネットワークにのってるので、お好みのパッケージマネージャでインストールします。
pnpm add -D octokit @octokit/types
尚、Octokit の TS 型定義は、@octokit/types を使いました
次に、アクセストークンの発行ですが、これは、GitHub の Personal Access Token を使う方法と、GitHub アプリをつくるほうほうがあるらしいのですが、今回は、前者の Personal Access Token(PAT)を使おうと思います。
GitHub にログインして、Settings → Developer Settings → Personal access tokens → Tokens (classic) と進みます。
- Note
- 何用のトークンかを説明するテキストです。今回はサイト用なんで、
PAT for no7.spaceみたいな感じの名前をつけました。
- 何用のトークンかを説明するテキストです。今回はサイト用なんで、
- Expiration
- トークンの有効期限です。この日数が経過すると自動的に無効になります。
- 設定しておくと万が一トークンが漏れた場合でもこの日数までで被害を抑えられる可能性がありますが、期限が切れるたびに更新する必要があります。
- 一応無期限とする
No expirationも選択できますが、GitHub としては、期限を設定することを強く推奨しているようです。
- Select scopes
- このトークンでアクセス可能な領域を設定できます。
- とりあえず
repoがあれば動くとは思いますが、目的に応じてもうちょっと細かく設定しても良さそうです。 - 今回はリポジトリに限定してますが、プロフィール欄などに自分の GitHub プロフィールを引用したい場合は、
userのread:userとかuser:followあたりがあっても良いかもしれませんね。
一通り設定できたら、Generate token ボタンをクリックするとトークンが生成されます。
Warning
ここで表示されるトークンは、このタイミングでしか確認できません。忘れずに安全な場所に控えておきましょう。
万が一トークンがわからなくなった場合は、Regenerate token ボタンから再生成は可能ですが、その場合このトークンを使ってる場所全てでトークンを新しいものに更新する必要があります。
取得したトークンは、ローカルでは gitignore された .env に、そして今回サイトは Vercel にデプロイしてるので、デプロイ用は Vercel なので、プロジェクトの Environment Variables に入れました。
キーはシンプルに GITHUB_PAT としました。
今回、ビルド時にデータを取ってしまい、公開時は GitHub に接続しないのでトークンはクライアントサイドに公開する必要がありませんので、NEXT_PUBLIC_ プレフィックスは不要です。
GitHub とやり取りするヘルパーをつくる
あとで他のページでも使う可能性を考えて、GitHub とのやり取りは Next.js の page.tsx じゃなくて、
src/lib/github-helper.ts に置くことにしました。
import type { Endpoints } from '@octokit/types'
import { Octokit } from 'octokit'
export type RepoData = Endpoints['GET /user/repos']['response']['data']
export default function githubHelper() {
const octokit = new Octokit({
auth: process.env.GITHUB_PAT,
})
const getMyRepos = async (): Promise<RepoData> => {
try {
const response = await octokit.request('GET /user/repos', {
type: 'owner',
sort: 'updated',
direction: 'desc',
})
return response.data.filter(
(repo) => !repo.fork && repo.name !== 'nyagihime'
)
} catch (error) {
console.error('Error fetching repositories:', error)
return []
}
}
const getRequestRate = async (): Promise<number> => {
try {
const response = await octokit.request('GET /rate_limit')
return response.data.rate.remaining
} catch (error) {
console.error('Error fetching rate limit:', error)
return 0
}
}
return {
getMyRepos,
getRequestRate
}
}
getMyRepos
実際にリポジトリデータを取得する処理です。
エンドポイントは /user/repos で、タイプに owner を設定すると、自分がオーナーのもののみに絞れる(つまりフォークしたリポジトリは除外)ようになる…はずなんですが、うまくいってないのでこのあと手動で絞り込みます。
ソートはなんでもいいんですが、更新があったものを上に上げたいので、今回は updated とし、direction も desc としました。
で、最後にうまくフィルターできなかったけど今回一覧に載せる必要がないリポジトリをフィルターしていきます。
まずは、フォークしたリポジトリを除外します。
これは、レスポンスの中に fork というフラグがあるので、これがついてないものだけになるようにフィルターすればOKです。
もう1つの条件の repo.name !== 'nyagihime' ですが、これは、プロフィール用のリポジトリになります。
GitHub はアカウント ID と同名のリポジトリを作って README.md を設置するとそれがプロフィール欄に表示される仕組みがあるんですが、これは単にプロフィルであって、プロジェクトとは違うため除外しました。
response.data.filter((repo) => !repo.fork && repo.name !== 'nyagihime')
レスポンスの型は @octokit/types から Endpoints を読み込んで、Endpoints['GET /user/repos'] みたいにすると、このエンドポイントのレスポンスを得ることができますので、目的の階層までたどればOK。
今回は、Octokit からのレスポンスのうち、実際のデータ部分の型だけあればよかったので、こうなりました。
この階層でレスポンスを返して、フロント側から利用することになるんで、これはそのままそっくり Export しちゃいました。
export type RepoData = Endpoints['GET /user/repos']['response']['data']
getRequestRate
Note
これは、認証できてるかを確認するために作ったデバッグ用メソッドなので、あとで消してしまってOKなやつですが、同じような現象に遭遇したときのためにサンプルコードとしては残しておきます。
実はこのロジックを使って何度かデータを取得してるはずなのに PAT の管理画面で、トークンがいつまでたっても Never used のままという現象が起こっていて、認証できてるかを確認したくて用意したものです。
ちなみにこれ、成功するとトークンで認証されていれば 5000 またはそれに近い値(既にリクエスト数を消費してる場合など)が戻ります。
逆に、認証できてない場合は最大が 60 となるため、これを使ってちゃんと認証できてるかを知ることができます。
さいごに
ここまででとりあえずデータは取れるようになったので、適当に page.tsx つくって、スタイル当てればとりあえず一覧は出せるようになります。
が、見た目の部分は今回の本質からはそれると思うのでバッサリカット。
あとは、現状リクエスト内容は最小限で、per_page とかも指定してないし、ページ送りも未実装ですので、リポジトリが増える前にこのあたりも整えないとなぁ。
それと、リポジトリ内の言語の割合とかも表示したい(これはリポジトリ個別にリクエストしないと取れないらしい)
