Next.jsを使ってホットペッパーのグルメサーチAPIを表示する

API

今回は、Next.jsのgetServerSidePropsを使って、ホットペッパーのグルメサーチAPIを読み込んで表示させてみます。

APIキーをゲット

まず、リクルートWebサービスのAPIキーを発行します。

新規登録ページにてメールアドレスを入力すると、APIキーがメールで送られてきます。

新規登録ページにてメールアドレスを登録
新規登録ページにてメールアドレスを登録

メールに記載されたAPIキーは、後で使います。

ご利用案内にも目を通しておきましょう。

Next.jsのインストール

それでは、Next.jsをインストールします。

Create Next Appを使用します。

コマンドラインで

npx create-next-app nextjs-hotpepper-api --js

を打つと、インタラクティブモードでインストールが始まりますので、エンターキーを押して押して押して全てデフォルトにしました。

デフォルトだとTypeScriptでプロジェクトが作成されるため、今回は –js をつけてJavaScriptのプロジェクトにしました。

無事にインストールが完了したら、インストール先のディレクトリに移動して、コマンドラインで、

npm run dev

と打って、http://localhost:3000/に移動すると、デフォルトのNext.jsアプリが表示されます。

デフォルトのNext.jsアプリ
デフォルトのNext.jsアプリ

この状態から編集していきます。

不要なコードを削除

まず、デフォルトのCSSファイルの中身のコードを削除します。

/styles内にある、globals.cssとHome.module.cssの中身を全削除。

次に、/pages内にある、index.jsファイルも不要なコードを削除します。

<main>の下を全てを削除して、その他不要なコードも削除。

index.jsは、ひとまずこんな感じになりました。

import Head from "next/head";
import styles from "@/styles/Home.module.css";

export default function Home() {
  return (
    <>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className={styles.main}></main>
    </>
  );
}

APIキーを.env.localに保存

APIキーをそのままコードに書いてしまうと、ブラウザだったりGitHubだったりから見られてしまうので、.env.localというファイルに記載してから、後でprocess.envで呼び出して使います。

.env.localをルートディレクトリに作成します。

.env.local
API_KEY=xxxxxxxxxxxxxxxx  # 先程取得したAPIキー

保存したら次行きましょう。

getServerSidePropsを使ってAPIを読み込む

Using getServerSideProps to fetch data at request timeを参考に、/pages/index.js内にコードを書いていきます。

いったん店名をリスト表示するところまで書いてみます。

サービスエリアの絞り込みで福岡を指定しています。サービスエリアマスタ参考

index.js
import Head from "next/head";
import styles from "@/styles/Home.module.css";

export default function Home({ results }) {
  return (
    <>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className={styles.main}>
        <h1>ホットペッパー 福岡グルメサーチ</h1>
        <ul>
          {results.shop.map((data, i) => {
            return <li key={i}>{data.name}</li>;
          })}
        </ul>
      </main>
    </>
  );
}

// リクエストごとに呼び出されます。
export async function getServerSideProps() {
  const apiKey = process.env.API_KEY;
  const baseUrl = "https://webservice.recruit.co.jp/hotpepper/gourmet/v1/";
  const serviceAria = "SA91"; // サービスエリアを指定
  const format = "json"; // デフォルトがXMLフォーマットなのでJSONを指定

  // 外部APIからデータをFetchします。
  const res = await fetch(
    `${baseUrl}?key=${apiKey}&service_area=${serviceAria}&format=${format}`
  );
  const json = await res.json();
  const { results } = json;

  // 上の方にあるHomeにpropsとしてデータが渡されます。
  return { props: { results } };
}

これをブラウザで確認するとこんな感じです。

ひとまず店名をリスト表示
ひとまず店名をリスト表示

results.shopは配列なので、.map()で展開しています。

.map()内のコールバック関数の第2引数の”i”は、1,2,3・・・とカウントアップしていくので、Reactアプリのkeyプロパティとして使いやすいです。

他の要素も取得してみる

ひとまず店名にアンカーリンクをつけて、画像、キャッチコピー、住所、営業時間を表示してみます。

(※時間が空いたため上と店名が異なっています)

店名以外の要素も取得
店名以外の要素も取得

コードはこんな感じです。(前後は省略)

index.js
import Head from "next/head";
import Image from "next/image";
import styles from "@/styles/Home.module.css";

省略

      <main className={styles.main}>
        <h1>ホットペッパー 福岡グルメサーチ</h1>
        <ul>
          {results.shop.map((data, i) => {
            return (
              <li key={i}>
                <a href={data.urls.pc}>
                  <Image
                    src={data.photo.pc.m}
                    alt={data.name}
                    width={168}
                    height={168}
                  />
                </a>
                <div>{data.catch}</div>
                <h3>
                  <a href={data.urls.pc}>{data.name}</a>
                </h3>
                <div>{data.address}</div>
                <div>営業時間:{data.open}</div>
              </li>
            );
          })}
        </ul>
      </main>

省略

Imageタグについて

上記のコードではimgタグの代わりにNext.jsのImageタグを使っています。
コードの上の方で import Image from "next/image"; と読み込んでいます。

Imageタグを使ったほうが、いろいろパフォーマンスが上がるとのことです。

他所(リモート)から画像を読み込む場合は、next.config.json内にremotePatterns追記が必要で、画像のwidthとheightのサイズ指定も必要です。

next.config.jsonは、Create Next Appで自動的にルートディレクトリに作成されているはずです。

編集します。

next.config.json
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "imgfp.hotp.jp",
      },
    ],
  },
};

module.exports = nextConfig;

images: {} 以下を追記してあります。

hostnameは、読み込む画像のドメイン部分です。

CSSで装飾する

あとはクラス名を割り振って、簡単にCSSで飾り付けます。

完成したものはこちら。

以下、コードです。

index.js
省略

      <main className={styles.main}>
        <h1>ホットペッパー 福岡グルメサーチ</h1>
        <ul className={styles.shopList}>
          {results.shop.map((data, i) => {
            return (
              <li key={i} className={styles.shop}>
                <div className={styles.leftSide}>
                  <a href={data.urls.pc}>
                    <Image
                      src={data.photo.pc.m}
                      alt={data.name}
                      width={168}
                      height={168}
                    />
                  </a>
                </div>
                <div className={styles.rightSide}>
                  <div className={styles.catch}>{data.catch}</div>
                  <h3 className={styles.name}>
                    <a href={data.urls.pc}>{data.name}</a>
                  </h3>
                  <div className={styles.address}>
                    <span className={styles.bold}>住所:</span>
                    {data.address}
                  </div>
                  <div className={styles.open}>
                    <span className={styles.bold}>営業時間:</span>
                    {data.open}
                  </div>
                </div>
              </li>
            );
          })}
        </ul>
      </main>

省略
global.css
body {
  margin: 0;
  padding: 0;
}

a {
  color: #146c94;
  text-decoration: none;
}

main {
  max-width: 900px;
  margin: 0 auto;
  line-height: 160%;
}
Home.module.css
.shopList {
  list-style: none;
  padding: 0;
  border-top: 1px solid #ccc;
}

.shop {
  border-bottom: 1px solid #ccc;
  padding: 10px 0;
  margin: 20px 0;
  display: flex;
}

.leftSide {
  padding: 5px;
}

.rightSide {
  padding: 5px;
}

.name {
  font-size: 20px;
  margin: 10px 0;
}

.catch {
  color: #444;
  font-size: 14px;
  font-weight: bold;
}

.address,
.open {
  color: #444;
  font-size: 14px;
}

.bold {
  font-weight: bold;
}

以上です。

コメント

タイトルとURLをコピーしました