Github issueをCMSとして使う
2021/10/28
JAMJAMJAMStack/Online #2
jiyuujin
- 複雑なUIを作るのが好きです
- React/Next/Vue/Nuxt/Scala/Swift
- スタッフとして色々と参画中
- FlutterKaigi 2021
- JAWS DAYS 2021
- Flutter Japan User Group
- PWA night
- v-kansai / kansai.ts
- プロフィールサイト
- https://yuma-kitamura.nekohack.me
- 技術ブログ
- https://webneko.dev
URL:
https://ohayo.nekohack.me
Deploy to Vercel
Fetch issues
Vite
Vue
Twitter Spaces で
雑談する
こんな
イメージ
そもそも Spaces とは
- 今年春、部分的に開始された音声のみでやりとりを行うコミュニケーションツール
- 共同ホスト最大 2 名、スピーカー最大 10 名
- スケジューリングも可能
- 最近ホスト権が全開放された
- なお、引き続き PC (PWA) 版の Web ブラウザにおいてはスピーカーとして参加できない
- リスナーとしてのみ参加が許されている
いつやっているの?
- 平日の毎朝、 09:00 から 45 分間程度
- 扱うテーマは基本的にフリー
- 前日に勉強会が開催されたらそれを振り返ったり、誰かがブログを書いたらその知見共有と宣伝を兼ね雑談したり、と様々
- 私自身の職歴が iOS 開発より始まって Android 開発も経ながら、現在は React (Vue) を中心とする Web フロントエンド開発に携わっている
- 自然とその辺りが中心になっている傾向
- 最近は Flutter 開発にも取り組む
これまでは Notion を
- 今年の GW 明けよりこの活動をスタートした
- かれこれ半年が経過している
- 参加できない方、これから回数を重ねて振り返ることを想定することを考慮した
- 当時 API が Public Beta になったと話題の Notion に雑談内容を記録した
- https://www.notion.so/nekohack/Quick-Note-c0a6b685fb524ca4823cc1dccbf2f9b8
- 当時 API が Public Beta になったと話題の Notion に雑談内容を記録した
なぜ Github issue に
- 課金しなくても良い
- Github issue を使う前は Notion を使っていた
- チームプランで始めてしまったがゆえ、日に日に容量と相談
- 個人プランに鞍替えすれば無料で行けそうだった
- Github issue を使う前は Notion を使っていた
- 誰でも issue を書ける
- API としての完成度が高い
- 良くも悪くもまだ Notion API はベータ版
- タイトルやラベル付けの取得は問題無かった
- 肝心となるコンテンツの部分 (ブロックの中身) を完全な形式で取得できなかった
- 良くも悪くもまだ Notion API はベータ版
具体的にやったことは
ざっくりこの 3 点
- 平日は毎朝、自動で Github issue を作成する
- Google Apps Script
- AM8:00 (JST+0900) 台にトリガーを設定する
- Google Apps Script
-
GraphQL を使えるよう設定する
- @vue/apollo-composable
- Vite (Vue 3) で SG 化
- きっかけは仕事の場面でバンドラとして CRA 環境のビルド高速化に Vite を利用した経験から
- antfu/vite-sse を参考にさせていただいた
- 結果としては中々に面倒な印象でした
- 後日ブログで詳しく
- きっかけは仕事の場面でバンドラとして CRA 環境のビルド高速化に Vite を利用した経験から
Vue 3 で GraphQL を使う
- Github API に v3 (REST) と v4 (GraphQL) が存在する
- 違いは REST と GraphQL の書き味
- 気になるリクエスト制限
- v3 は 5000 リクエスト数 / 1h まで、一方 v4 は 5000 ノード数 / 1h まで
- @vue/apollo-composable を使えば Hooks ベースで issue データを fetch できる
export const searchQuery = gql`
query {
viewer {
login
repository(name: "ohayo-developers") {
createdAt
name
issues(last: 100 orderBy: { field: CREATED_AT, direction: DESC }) {
nodes {
id
body
createdAt
title
url
number
labels(last: 10) {
nodes {
id
name
}
}
timelineItems(first: 10) {
nodes {
... on IssueComment {
id
body
}
}
}
participants(last: 10) {
nodes {
id
login
name
avatarUrl(size: 40)
}
}
}
}
}
}
}
`
必要なものだけ取得する
- タイトル
- 作成日時
- Label 一覧
- コメント内容
- 参加者 (コントリビュータ) 一覧
export const searchQuery = gql`
query {
viewer {
login
repository(name: "ohayo-developers") {
issues(last: 100 orderBy: { field: CREATED_AT, direction: DESC }) {
nodes {
timelineItems(first: 10) {
nodes {
... on IssueComment {
id
body
}
}
}
}
}
}
}
}
`
コメント内容を取得する際は、注意が必要
- GraphQL の Union 型を扱う
- 事前定義のスキーマに対応したフラグメントを読み込む必要がある
{
"__schema": {
"types": [
{
"kind": "UNION",
"name": "IssueComment",
"possibleTypes": [
{
"name": "IssueComment"
}
]
}
]
}
}
トークンが無いとダメ
- 事前に Github の管理画面でトークンを発行する
- admin:repo_hook という scope を指定してトークン発行の手続きをとる
GraphQL 用 Client を準備
- フロントエンドで以下の通り構築する
- 必要な依存関係をインストールする
- 事前定義のスキーマに対応したフラグメントを読み込む必要がある
- ApolloClient のキャッシュで読み込む
- これが無いと issue のタイトルまでしか取れずコメントの内容までは取れない
- ApolloClient のキャッシュで読み込む
import fetch from 'isomorphic-unfetch'
import ApolloClient from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { ApolloLink, concat } from 'apollo-link'
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import introspectionQueryResultData from './fragmentTypes.json'
const GITHUB_API_V4 = 'https://api.github.com/graphql'
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData
})
const authMiddleware = new ApolloLink((operation, forward) => {
operation.setContext({
headers: {
Authorization: `bearer ${
import.meta.env.VITE_APP_GITHUB_API_ACCESS_TOKEN
}`,
Accept: 'application/vnd.github.v4.idl'
}
})
return forward(operation)
})
const httpLink = new HttpLink({
uri: GITHUB_API_V4,
fetch
})
export const apolloClient = new ApolloClient({
link: concat(authMiddleware, httpLink),
cache: new InMemoryCache({
fragmentMatcher
})
})
コンポーネントで使う
- 先に作成したクエリと Plugin を合わせ、適宜呼び出したいコンポーネントで呼び出す
- @vue/apollo-composable の useQuery でクエリを読み込む
- useResult でレスポンスが吐き出される
- ここから先はよしなりにデータを加工してページの描画に繋げる
import { useQuery, useResult } from '@vue/apollo-composable'
import { searchQuery } from '../graphql/issue'
export default {
setup() {
const { result, error, loading } = useQuery(searchQuery)
const issues = useResult(
result,
null,
(data) => data.viewer.repository?.issues?.nodes
)
return { loading, error, issues }
}
}
そしてページ描画へ
- データを取得できたら、あとはよしなりにそのデータを加工してページ描画に繋げる
- 今回は grid レイアウトを使ってカレンダーを作った
最後にこれだけは!
- API の使い心地を考える
- Notion の書き味は申し分無し、しかし現時点では API が完全でない気がする
- Vite を静的サイトで使ってみて
- 設定するのはちょっと面倒臭かった
- 詳しくは後日ブログ書く🤙🏻
- 設定するのはちょっと面倒臭かった
- 雑談という取り組みから
- 「誰でも参加 (編集も) できる」を目指して
- 雑談だけではなく日々の知識の蓄積などでも?
- お時間ある方は 5 分間耳だけでも聴いてみて
各リポジトリはこちら
- ドキュメント
- どなたでもお書きいただけます!
- https://github.com/jiyuujin/ohayo-developers/issues
- どなたでもお書きいただけます!
- ウェブサイト
- コントリビュート歓迎!
- https://github.com/jiyuujin/ohayo-website
- コントリビュート歓迎!
Thank you..🙇♀️
Github issuesをCMSとして使う
By jiyuujin
Github issuesをCMSとして使う
DevRel/Online #3 では Flutter Osaka を中心に
- 546