【Next.js】getServerSidePropsでクエリパラメータを取得する方法

前回、Next.jsを使ったクライアントサイドでのクエリパラメータを取得する方法を解説しました。

[Next.jsでクエリパラメータを遷移先に渡すにはどうすればいいのか？ - Qiita](https://qiita.com/swallow0708/items/52fbe7480b2b9a54db0c)

前回使用した`useRouter`フックはクライアントサイドで動きますが、サーバーサイドではエラーになります。そのためサーバーサイドでクエリパラメータを取得するには別の方法が必要です。

なので、**今回はサーバーサイドでクエリパラメータを取得する方法**を解説します。

## サーバーサイドレンダリングでクエリパラメータを取得する方法

まずはサーバーサイドでレンダリングするため`getServerSideProps`の定義が必要です。その際の注意点としては**`export`と`aync`キーワードをつけること**です。これにより、リクエスト毎にこのページを事前にレンダリングします。

そして、この`getServerSideProps`の中で`useRouter`を使おうとするエラーが発生します。

![WS000002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621361/2da5b2fa-3daa-d961-5cd9-0ea03f101990.jpeg)

これはサーバーサイドレンダリングでは`useRouter`を使えないためです。クライアントサイドで使う前提になっています。

ここで使えるのが`getServerSideProps`の引数です。通常`context`という名前で設定しますが、どんな引数名でも使えるはずです。

この`context`の中には

- **req：HTTPリクエストオブジェクト**
- **res：HTTPレスポンスオブジェクト**
- **query：クエリパラメータ**

が入っています。この中の`query`の中にクエリパラメータの値が格納されているので、この値を使えばサーバーサイドレンダリングでも使うことができます。

たとえば、次のページを作る際には2つのページが必要になります。

![Nextjsのパラメータ引き渡し(サーバーサイドレンダリング).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621361/343f1fb2-4893-f0da-108c-ff247373018a.gif)



1. **クエリパラメータを送るページ**
2. **クエリパラメータを受け取るページ**

クエリパラメータを受け取るページでは、受け取った値でWebAPIを呼び出してGoogleブックの検索結果を表示しています。

## クエリパラメータを送るページ

```index.jsx
import { useRouter } from 'next/router';
import {useState} from 'react';

export default function Index() {
  const router = useRouter();                //ルーターの取得
  const [keyword, setKeyword] = useState();  //検索キーワード

  // ボタンをクリックしたときの処理
  const clickButton = () => {
    //未入力の時
    if (!keyword) {
      return;
    }

    router.push({
        pathname:"/result",       //URL
        query: {keyword :keyword} //検索クエリ
      });
  }

  return (
    <div style={{textAlign: "center", marginTop: "50px"}}>
      {/* 入力項目 */}
      <input 
        type="text" 
        value={keyword}
        onChange={(e) => setKeyword(e.target.value)} /*変更時keywordに値をセット*/
      />

      {/* ボタン */}
      <button 
        onClick={clickButton}
        disabled={!keyword}>    {/*入力項目が未入力の場合、非活性*/}
        検索
      </button>
    </div>
  )
}
```

クエリパラメータを送信するページでは`useRouter`フックを使って画面遷移とクエリパラメータの設定を行っています。`router.push`メソッドの引数にURLとクエリパラメータをセットしています。

- **`pathname`：URL**
- **`query`：クエリパラメータ**

注意点としてはクエリパラメータは連想配列で設定します。複数セットする可能性があるためです。

## クエリパラメータを受け取るページ

```jsx
//サーバーサイドレンダリング
export async function getServerSideProps(context) {
  //検索キーワード
  const keyword = context.query.keyword;

  //Googleブックで検索
  const response = await fetch (encodeURI(`https://www.googleapis.com/books/v1/volumes?q=${keyword}`));

  return {
    props: {
      bookList: await response.json(),  //Googleブックの戻り値
      keyword: keyword,                 //検索キーワード
    }
  }
}

export default function Result(props) {

  //Googleブックの情報を取得
  const bookList = props.bookList.items.map(item => item.volumeInfo).map(((item) => {
    return {
      title : item.title,                             //タイトル
      url   : item.infoLink,                          //URL
    }
  }));

  return (
    <div style={{marginLeft: "50px", marginTop: "50px"}}>
      <h1>検索キーワード：{props.keyword}</h1>

      {/* 検索データの表示 */}
      <ul>
        {bookList.map((item) =>
          <li>
            <a href={item.url}>{item.title}</a>
          </li>
        )}
      </ul>
    </div>
  )
}
```

一方、遷移先ページでは`getServerSideProps`メソッド内でクエリパラメータを取得してgoogleブックで検索した結果をリストで表示しています。

**クエリパラメータは`context.query.keyword`で取得**しています。`keyword`の部分は遷移元で設定した名前です。

後は、`getServerSideProps`の戻り値の`props`にクライアントサイドに渡したい値をセットして表示するだけです。クライアント側で`bookList`変数で取得した検索結果を加工して、map関数を使ってリスト表示しています。

## まとめ

- `router.push`の`pathname`に遷移先URL、`query`にクエリパラメータをセットする
- `getServerSideProps`を使うことでサーバーサイドレンダリングを利用できる
- 引数の`context.query`内に遷移元から送られたクエリパラメータが取得できる