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では、
ZenngetServerSideProps
またはgetStaticProps
関数をexportすることで、リクエストごとまたはビルドごとにサーバー側で処理を行うことができます。
リクエスト時やビルド時にデータ取得のスクリプトが走る、と理解しました。
getServerSideProps
やgetStaticProps
関数は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.prototype.then() – MDNPromise
オブジェクトのメソッドで、Promise
が成功した場合と失敗した場合のコールバック関数を取る。
失敗時にはcatchを使うのが現実的。
取得したデータを表示する
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/配下のファイルにて
getServerSideProps
とfetchを使ってデータを取得しブラウザに表示することができる - 静的サイトと同じく、npx next buildで動的アプリをビルドできる
- 静的サイトと同じく、npx next startでビルドしたアプリを実行できる
おまけ:エラーページ作成方法
- カスタム 404 ページを作成するには、
pages/404.js
ファイルを作成します。このファイルはビルド時に静的に生成されます。 - 500 ページをカスタマイズするには、
pages/500.js
ファイルを作成します。このファイルはビルド時に静的に生成されます。 - エラー分岐する場合はErrorコンポーネントをpages/_error.jsで上書きすることも可能。
pages/_error.js
は本番環境でのみ使用されます。
特に作成しない場合、デフォルトでこのような画面が表示されます。