AsaDesign

Next.jsワークショップやってみた②動的アプリ編

参考サイト:Next.jsでAPIやサーバー側処理のあるサイトを作ってみよう -Zenn

目標

  • Next.jsのAPIを動かしてみる
  • データを返すAPIを実装する
  • APIでデータを取得する
  • 動的アプリをビルドする
  • ビルドしたアプリを実行する


curl http://localhost:3000/api/helloを実行するとpages/api/hello.tsの内容が返ってくる

 % curl http://localhost:3000/api/hello
{"name":"John Doe"}

⚠️【エラー小話】:next.config.mjsに「output: ‘export’,」と記載したままだと404エラーを返す

静的サイトをビルドする際にoutputモードをexportにする必要があるのですが、そのままだとcrulコマンドを実行しても404エラーとなります。

npm run dev時に以下のエラーも出ます。

API Routes cannot be used with “output: export”. See more info here: https://nextjs.org/docs/advanced-features/static-html-export
「APIルートは「output:export」では使用できません。」

→「output: ‘export’,」を削除して再実行するとステータス200でデータを返してくれました。

pages/api配下にAPIを追加する

pages/api配下にproducts.tsファイルを作成し、GETリクエストでJSONデータを返すAPIを追加します。
end()では、レスポンスbodyを持たないことを定義しています。

import { NextApiRequest, NextApiResponse } from "next";

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  // GET以外のリクエストを許可しない
  if (req.method?.toLocaleLowerCase() !== 'get') {
    return res.status(405).end()
  }
  res.status(200).json(
    [
      {
        name: "サバ缶",
        price: 150,
      },
      {
        name: "ツナ缶",
        price: 300,
      }
    ]
  )
}

curlコマンドを実行すると、データを取得することができました。

% curl -i http://localhost:3000/api/products 
HTTP/1.1 200 OK
...
[{"name":"サバ缶","price":150},{"name":"ツナ缶","price":300}]

postだと405エラーを返します。

% curl -I  http://localhost:3000/api/products -XPOST
HTTP/1.1 405 Method Not Allowed

getServerSidePropsとfetchでデータを取得する

手動でcurlコマンドでデータを取得できたので、次はNext.js内で取得させます。

Next.jsでは、getServerSidePropsまたはgetStaticProps関数をexportすることで、リクエストごとまたはビルドごとにサーバー側で処理を行うことができます。

Zenn

リクエスト時やビルド時にデータ取得のスクリプトが走る、と理解しました。

getServerSidePropsgetStaticProps関数はpagesディレクトリ配下でしかexportできず、コンポーネント単位で処理を定義することはできないそうです。(?)

export async function getServerSideProps (context: { req: { headers: { host: string; }; }; }) {
  try {
    const host = context.req.headers.host || 'localhost:3000'
    const protocol = /^localhost/.test(host) ? 'http' : 'https'
    const products = await fetch(`${protocol}://${host}/api/products`)
    .then(data => data.json())
    return {
      props: {
        products,
      }
    }
  } catch (e) {
    console.log(e)
    return {
      props: {
        products: [],
      }
    }
  }
}

then()って何だっけ?

非同期処理の完了(もしくは失敗)の結果およびその結果の値を表すPromiseオブジェクトのメソッドで、Promise が成功した場合と失敗した場合のコールバック関数を取る。
失敗時にはcatchを使うのが現実的。

Promise.prototype.then() – MDN

取得したデータを表示する

before

export default function HelloWorld() {

after

hello_world.tsx内で非同期でデータを取得し、同ファイルのメインコンポーネントにpropsとして渡しています。

type Props = {
  name: string,
  price: number,
}
export default function HelloWorld(props: Props) {
...
<pre><code>{JSON.stringify(props,null,2)}</code></pre>

curlコマンドを叩かなくても、アプリ内でデータを取得してブラウザに表示してくれるようになりました。

動的アプリをビルドする

静的サイトを作ったように、サーバー処理を含むアプリもビルドが可能

 % npx next build
 ...
 Route (pages)                              Size     First Load JS
  /                                      4.88 kB        93.2 kB
    css/46cbeeb393582c7c.css             1.18 kB
   /_app                                  0 B            88.3 kB
  /404                                   180 B          88.5 kB
 ƒ /api/hello                             0 B            88.3 kB
 ƒ /api/products                          0 B            88.3 kB
 ƒ /hello-world                           469 B          88.7 kB
+ First Load JS shared by all              119 kB
   chunks/framework-64ad27b21261a9ce.js   44.8 kB
   chunks/main-f61b188e1c4e2fda.js        32.1 kB
   chunks/pages/_app-d415f9c498cd9349.js  10.5 kB
   css/6c44dc282b4d2fb1.css               30.6 kB
   other shared chunks (total)            818 B

  (Static)   prerendered as static content
ƒ  (Dynamic)  server-rendered on demand

アプリを起動する

% npx next start
トップページ
別のページも表示されている

⚠️【エラー小話】初回、以下のエラーが出てブラウザも500エラーが表示されて困ったのですが、もう一度ビルドし直すとエラー解消されました。

Error: Cannot find module ‘/Users/~省略~/.next/server/pages/index.js’
.next/server/pages/index.js’が見つからないです」

まとめ

  • pages/api/配下にAPIを実装できる
  • pages/配下のファイルにてgetServerSidePropsfetchを使ってデータを取得しブラウザに表示することができる
  • 静的サイトと同じく、npx next buildで動的アプリをビルドできる
  • 静的サイトと同じく、npx next startでビルドしたアプリを実行できる

おまけ:エラーページ作成方法

Custom Errors – Next.jsより引用👇

  • カスタム 404 ページを作成するには、pages/404.jsファイルを作成します。このファイルはビルド時に静的に生成されます。
  • 500 ページをカスタマイズするには、pages/500.jsファイルを作成します。このファイルはビルド時に静的に生成されます。
  • エラー分岐する場合はErrorコンポーネントをpages/_error.jsで上書きすることも可能。pages/_error.jsは本番環境でのみ使用されます。

特に作成しない場合、デフォルトでこのような画面が表示されます。