今回は、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アプリが表示されます。

この状態から編集していきます。
不要なコードを削除
まず、デフォルトの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;
}
以上です。


コメント