Next.js+SWRでGraphQL!
作ったもの
実行方法は README.md をご確認ください。
実行するとトップページに二つリンクが表示されます。
countries
をクリックすると国名の一覧を取得して表示します。
issues
をクリックするとoctocat さんの Hello-World リポジトリの Issueを 100 件取得して表示します。
取得先はGitHub の GraphQL APIです。
簡単に解説
contries
pages/countries.tsxで全て完結しています。
重要なとこだけかいつまんで解説すると
import { request } from "graphql-request";
import useSWR from "swr";
const API = "https://countries.trevorblades.com"; // GraphQLエンドポイントのURL
const getCountries = () => {
const { data, error } = useSWR<FetchData>(query, (query) => //👈ポイント
request(API, query)
);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return data.countries.map((country) => (
<li key={country.code}>{country.name}</li>
));
}
useSWR
の第一引数に GraphQL の query 文字列、第二引数に Fetcher を設定しています。
戻り値のdata
には取得データが格納され、error
にはエラー情報が格納されます。
またそれぞれの状態から Loading 状態も判定可能です。
issues
こちらはリクエストする際に header や variable が必要な場合のサンプルです。
事前にGitHub のアクセストークンを取得して.env.local
を設定しておく必要があります(詳しくはREADME.md参照)。
こちらもかいつまんで解説すると
import { GraphQLClient } from "graphql-request";
import useSWR from "swr";
const API = "https://api.github.com/graphql"; // GraphQLエンドポイントのURL
const repositoryOwner = "octocat"; // 取得するリポジトリ所有者のユーザー名
const repositoryName = "Hello-World"; // 取得するリポジトリの名前
const issuesFirst = 100; // 取得するIssueの数
const getIssues = () => {
const client = new GraphQLClient(API, { //👈ポイント①
headers: {
Authorization:
"bearer " + process.env.NEXT_PUBLIC_GITHUB_PERSONAL_ACCESSTOKEN,
},
});
const { data, error } = useSWR<FetchData>(
[getRepositoryQuery, repositoryOwner, repositoryName, issuesFirst],
(query, owner, name, first) => //👈ポイント②
client.request(query, {
repositoryOwner: owner,
repositoryName: name,
issuesFirst: first,
})
);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return data.repository.issues.edges.map((issue) => (
<li key={issue.node.id}>{issue.node.title}</li>
));
}
(ポイント ①)header と variable の設定が必要なので Fetcher にはGraphQLClient
を使用しています。
(ポイント ②)useSWR
の第一引数は query と各 variable を配列で渡し、第二引数でそれらを使用します。
ここが大事なポイントで SWR は取得した値をキャッシュしますが、useSWR の第一引数に与えた値をキャッシュの Key として扱います。
もし variable の値を第一引数で渡していない場合、同じ query を variable 違いで実行したとき、本来サーバーへリクエストすべきところがキャッシュの値を返却してしまいます。
仕様上絶対に毎回同じ値になる variable は引数で渡す必要は無いですが(今回のケースだと「repositoryOwner
とrepositoryName
は毎回変わるがissuesFirst
は毎回 100 で固定」 など)、
バグに直結する部分でもあるので variable は必ず引数で渡す、などルールを決めても良いかもしれません。
この Key はデータ更新(mutation)の際にも使用することになります。
その内容については以下の記事で実践していいます。
最後に
実務(CRA)では GraphQL クライアントに ApolloClient を使用しておりかなり便利で気に入っているのですが、Next.js で ApolloClient を使用する場合ページのレンダリングと API リクエストのタイミングを注意深く意識してコーディングしなければなりません。
そのあたりの内容は以下のブログで解説されています。
Next.js においては SWR の方が気楽に書けるかなと思いますが、GraphQL を扱う上で Normalized cache の有無の差は非常に大きいため、どちらを採用するかは実装内容と照らし合わせてよく検討する必要があります。
Discussion