Next.jsのAPI Routesを使ってmicroCMSの検索APIを作成する

みなさん microCMS 使ってますか？ めちゃくちゃ便利ですよね！
最近個人開発で使っていて記事の検索ページを追加したいな〜と思っていたら、全文検索機能で簡単に実装する事が出来たので、メモがてら実装例を紹介したいと思います。

前述の通り microCMS には全文検索機能があり、qパラメータにキーワードを渡すことで、簡単に検索APIを実装する事が出来ます。
クライアント側で API をリクエストすると、 APIキーが漏れてしまうリスクがある為、サーバーサイドで実行するのが一般的なやり方だと思います。　　

実際に公式サイトでも vercelのserverless function を利用した実装方法が紹介されています。
そこで今回は Next.js の API Routes を使った全文検索APIの実装例を紹介しようと思います。

## microCMSデータの作成

最初に検索対象となるデータを作成しましょう。作成済みの方は飛ばしてしまってください
以下リンクから microCMS にログインしてください。アカウントがない方は登録してください。料金プランは hobby で問題ないです🙆‍♂️

https://app.microcms.io/signin

ログインが出来たら、サービスを作成しましょう。
今回の例では、書籍管理API を作成して、本の情報を取得してみようと思います。

![スクリーンショット 2022-01-31 15.08.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/d7d3ce9e-00cc-9dd8-74d8-79ad54c6b6fb.png)


つづいて API を作成していきます。API 名に書籍、エンドポイントに books を設定してください。

![スクリーンショット 2022-01-31 15.09.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/91904622-4a07-a7ee-9b72-78d8c7bca244.png)

API形式はリスト形式を選択してください。

![スクリーンショット 2022-01-31 15.11.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/30279323-e808-6464-2dc7-8d385612964c.png)

API を作成したらインターフェースを定義しましょう。今回は本のタイトルと概要のみを持たせます。

![スクリーンショット 2022-01-31 15.13.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/b23b6cc9-4c8a-b9f1-b52c-3149f0ccb0e1.png)

最後に書籍データを追加しましょう。追加するをクリックして適当な書籍のデータを登録してください。
入力後に公開ボタンを押す事で登録する事が出来ます。

![スクリーンショット 2022-01-31 15.21.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/38a3061e-15bc-8e77-af02-9246a6d454cb.png)

例として、2つの本を登録しました。

![スクリーンショット 2022-01-31 15.24.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/dd710ff4-3af7-7e84-1e30-8d16eadb82c6.png)
![スクリーンショット 2022-01-31 15.24.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/2bb3fb8e-7514-2881-a8b3-f7d378cb58bf.png)

これでテストデータの作成は完了です 🎉

## microcms-js-sdk の設定

続いて API をリクエストする準備をしましょう。既に Next.js の環境構築が済んでいる前提で進めていきます。
環境がない人は、以下を読んで Next.js プロジェクトを用意してから戻ってきてください！

https://nextjs-ja-translation-docs.vercel.app/docs/getting-started

まずは microCMS の sdk で必要となる環境変数を設定しましょう。
プロジェクト直下に .env ファイルを作成して以下を入力してください。

```
//.env
NEXT_PUBLIC_SERVICE_DOMAIN=ここにサービスドメイン値を入力
NEXT_PUBLIC_SERVICE_API_KEY=ここにサービスAPIキーを入力
```

 NEXT_PUBLIC_SERVICE_DOMAIN には管理画面サイドバーの上部に表示されている値を入力します。
![スクリーンショット 2022-01-31 15.39.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/8d6c5223-92fa-e02d-2d74-5b207ee3e21b.png)

NEXT_PUBLIC_SERVICE_API_KEY にはサイドバーの歯車をクリック後に APIキーをクリックして表示される APIキーを入力します。

![スクリーンショット 2022-01-31 15.36.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/158f7446-362f-d266-1caa-724277322972.png)

![スクリーンショット 2022-01-31 15.42.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/92212e93-591d-2526-0174-76fcddc830bf.png)

続いて、プロジェクトに microcms-js-sdk をインストールします。

```node
npm i microcms-js-sdk
```

インストールが完了したら、libs フォルダー配下に client.js を作成して SDK を初期化しましょう。

```javascript
// libs/client.js
// microCMS公式より抜粋
import { createClient } from 'microcms-js-sdk';

export const client = createClient({
  serviceDomain: process.env.SERVICE_DOMAIN,
  apiKey: process.env.SERVICE_API_KEY,
});
```

これでsdkの準備も完了です🎉


## 検索APIの作成

いよいよ検索APIを作成していきましょう。以下のように books 配下に index.js を作成してください。
pagesフォルダ -> api -> books -> index.js

👇 このようになっていれば、ばっちりです!
![スクリーンショット 2022-01-31 15.50.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/99abcfdb-fab3-8890-f7e1-df5f78790914.png)
作成したら index.js に以下を記述してください。
endpoint には API の基本情報を入力で設定したエンドポイントの値を渡してあげます。

```js
import {client} from "../../../libs/client";
export const getSearchBooks = async (req, res) => {
  // 検索したいキーワードをqueryから取得
  const keyword = req.query.keyword;

  // 検索キーワードを設定した状態でmicroCMSにリクエストを送信。
  const response = await client.get({
    endpoint: "books",
    queries: {
      q: decodeURI(keyword),
    },
  });
  return res.status(200).json(response);
};

export default getSearchBooks;

```

これで検索 API の作成が完了しました 🎉

## 検索APIの呼び出し

それでは、実際に API をリクエストして動作するか確認してみましょう！
リクエストに必要となる axios をインストールしましょう。

```
npm i axios
```

インストールが完了したら、 pages/index.js を以下のように置き換えてください！

```jsx
import {useState} from "react";
import axios from "axios";
import styles from "../styles/Home.module.css";

export default function Home() {
  const [keyword, setKeyword] = useState("");
  const [books, setBooks] = useState([]);

  const searchBooks = async () => {
    // 検索APIにリクエストを送信
    const res = await axios.get("/api/books", {
      params: {
        keyword,
      },
    });
    // 検索結果をセット
    setBooks(res.data.contents);
  };

  return (
    <div className={styles.container}>
      <div style={{display: "flex"}}>
        <input
          value={keyword}
          placeholder="キーワードを入力"
          onChange={(e) => setKeyword(e.target.value)}
        />
        <button onClick={searchBooks} style={{marginLeft: "4px"}}>
          検索実行
        </button>
      </div>
      <div style={{marginTop: "24px"}}>
        {books &&
          books.map((book, index) => (
            <div
              key={index}
              style={{
                border: "1px solid #E2E8F0",
                padding: "12px",
                marginTop: "8px",
              }}
            >
              <h2>{book.title}</h2>
              <p>{book.description}</p>
            </div>
          ))}
      </div>
    </div>
  );
}
```

検索欄に本のタイトルを入力して検索ボタンを押してデータが取得できていれば成功です 🎉
![スクリーンショット 2022-01-31 21.21.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/cf148dbd-e6da-a12d-f1cb-c223c8e39ac7.png)


また登録した本で共通するキーワードを入力すると該当する本が表示されるはずです!
![スクリーンショット 2022-01-31 21.22.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442365/6737e983-27a1-9078-fb50-37f547eb21b7.png)

うまくいかなかったりエラーが表示される場合は、どこかにタイポがないか確認してみてください！
それでも解決できない場合、エラー内容をコメントに書いていただければ私が一緒に悩みます 🐧


## 参考

Serverless Functions + microCMSで全文検索APIを作成する | microCMSブログ
https://blog.microcms.io/serverless-functions-search/

microCMS + Next.jsでJamstackブログを作ってみよう | microCMSブログ
https://blog.microcms.io/microcms-next-jamstack-blog/