Next.js の SG, ISR (with Apollo) の仕組みを作りながら学んでみた（ユーザー詳細ページに必要な getStaticPropsとgetStaticPathsについて）3/5

## はじめに

Next.js の SG + ISR (with Apollo) の仕組みを作りながら学んでみたシリーズ

このシリーズでは、Hasuraで用意した仮想のユーザーデータを外部APIとして取得しながら、

ユーザーの一覧ページと詳細ページを作りながら、SGと ISRを学ぶ記事です。

https://qiita.com/ryosuketter/items/ab7d4c8bfb19185f7b1e

今回は、ユーザーの一覧ページで表示させた各ユーザー詳細ページのリンク先になるページに必要な`getStaticProps`と`getStaticPaths`の実装について書きます。

## 目次

- 実装
    - 一覧ページ
        - [getStaticProps](https://qiita.com/ryosuketter/items/442db08856093e6cfeea)
        - [必要なモジュールのimport](https://qiita.com/ryosuketter/items/8c4820c06a441c717180)
        - [interface作成](https://qiita.com/ryosuketter/items/8c4820c06a441c717180)
        - [コンポーネント作成](https://qiita.com/ryosuketter/items/8c4820c06a441c717180)
    - 詳細ページ
        - [getStaticProps と getStaticPaths](https://qiita.com/ryosuketter/items/799d3330ab7cda37145c) 👈 今回はコレ
        - [必要なモジュールのimport](https://qiita.com/ryosuketter/items/cb2507559c6c71bf9f17)
        - [interface作成](https://qiita.com/ryosuketter/items/cb2507559c6c71bf9f17)
        - [コンポーネント作成](https://qiita.com/ryosuketter/items/cb2507559c6c71bf9f17)
- 検証
    - [作ったものをブラウザで見ながらSGとISRの仕組みをざっくり理解する](https://qiita.com/ryosuketter/items/af45c292249ea7a88d9d)

## SGという用語に関して

SSGと言ったり、SGと言ったりするかもしれませんが、SSGはかつての呼び方なのでSG（Static Generate）に統一して書こうと思います。

仕組みは同じものだと思ってください。

## 最終的なゴール

以下のような構成のアプリを作ることです。

![スクリーンショット 2021-10-18 17.27.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/e8a9a2e1-2723-4fec-77ef-be3b9eefdd81.png)

### 目的

- 仕事で使っている技術のキャッチアップと復習
- 使う可能性がある技術の理解度向上

## 詳細ページに必要なファイル作成

```
pages/
└── users/
    └── [id].tsx
```

なんでこの位置に？なんでブラケット付き？と言う疑問はこちらで👇

https://qiita.com/ryosuketter/items/442db08856093e6cfeea#dynamic-routes%E3%81%A8%E3%81%AF

## getStaticPropsとgetStaticPathsに必要なモジュール

```tsx
// getStaticPropsとgetStaticPathsの型
import { GetStaticProps, GetStaticPaths } from 'next'
import { initializeApollo } from '../../lib/apolloClient'
// GET_USERSIDS: 個別ページを取得するためにそのidを取得するクエリ
// GET_USERBY_ID: ユーザーidを引数にして特定のユーザーを取得するクエリ
import { GET_USERSIDS, GET_USERBY_ID } from '../../queries/queries'
// 上記のクエリの型（自動生成されたやつ）
import { GetUsersByIdQuery, GetUserIdsQuery } from '../../types/generated/graphql'
```

## getStaticPathsの実装

```tsx
export const getStaticPaths: GetStaticPaths = async () => {
  // apolloクライアントの作成
  const apolloClient = initializeApollo()
  // apollo.queryでユーザーIDのリストを取得
  const { data } = await apolloClient.query<GetUserIdsQuery>({
    query: GET_USERSIDS,
  })
  // ユーザーIDのリストから1つ1つmapで展開してpathsに格納
  // 事前にビルドするパスの対象を指定するためのもの
  const paths = data.users.map((user) => ({
    params: {
      id: user.id,
    },
  }))
  return {
    paths,
    fallback: true,
  }
}
```

### fallback: true について

まず`getStaticPaths`は`paths`と`fallback`が必須のパラメーターです。

関数 `getStaticPaths` の returnの中身の`fallback: true`を設定すると個別ページが動的に増えても対応できるようになります。

詳細を説明する前に`fallback`という単語に馴染みがなかったので用語の説明をします。

#### フォールバックという用語の説明

> フォールバックとは、通常使用する方式や系統が正常に機能しなくなったときに、機能や性能を制限したり別の方式や系統に切り替えるなどして、限定的ながら使用可能な状態を維持すること。また、そのような切り替え手順・動作のこと。

https://e-words.jp/w/%E3%83%95%E3%82%A9%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF.html

この説明を今回の実装に当てはめれば「事前に`paths`で指定されたパス以外に対してリクエストがあったときに、機能や性能を制限したり別の方式や系統に切り替える（Hasuraからデータを取得を部分以外がレンダリングされた静的なページを表示）などをすることで、

> 限定的ながら使用可能な状態を維持すること。

という感じかなと思います。コレでイメージしやすくなりますね。

---

`fallback`は事前にビルドしたパス以外にアクセスがあった時の動作を決めるものです。

- falseの場合は`404 page`となります
- trueの場合はアクセスされても404になりません

以下が、trueの場合で、ユーザーが事前にビルドしていないパスに対してアクセスがあった時の流れです

- useRouterを使い一時的にpageのfallbackバージョンを表示
- getStaticPropsがサーバー側で実行され静的なファイル（`A`とする）を生成
- `A`の生成が完了したらfallbackバージョンから切り替え、`A`を表示
- 以後のリクエストに対しては事前にビルドしたページ（`A`）を返す

## getStaticPropsの実装

```tsx
// 引数でparamsを受け取るようにします
export const getStaticProps: GetStaticProps = async ({ params }) => {
  // apolloクライアントの作成
  const apolloClient = initializeApollo()
  // apollo.queryで、引数で渡したidに一致するユーザー情報を取得
  const { data } = await apolloClient.query<GetUsersByIdQuery>({
    query: GET_USERBY_ID,
    variables: { id: params.id },
  })
  // ユーザーデータを返却
  return {
    props: {
      user: data.users_by_pk,
    },
    revalidate: 1,
  }
}
```

### revalidate: 1 について

`revalidate`を指定するとISRが有効化されます。

詳細はこちらで説明済みです。

https://qiita.com/ryosuketter/items/442db08856093e6cfeea#revalidate

気になる方は見てみてください。

## まとめ

今回は、ユーザーの一覧ページで表示させた各ユーザー詳細ページのリンク先になるページに必要な`getStaticProps`と`getStaticPaths`の実装について書きました。

## 次回

https://qiita.com/ryosuketter/items/cb2507559c6c71bf9f17

## アウトプット100本ノック実施中

https://qiita.com/ryosuketter/items/713474dbd009870e4103