Nextjs: API Routes

2022.06.09
Nextjsとは
Nextjsを使い始めたばかりはpageディレクトリの直下にファイルを追加すれば自動でルーティングしてくれるReactで書けるやつ。くらいの印象でNextjsのありがたみがよくわかっていませんでしたが、それはnext build && next exportで静的ファイルを作っていただけだったからかもしれません。こうやって作られた静的HTMLファイルはサーバーのどこかにそのままコピーされて、そのサーバーにアクセスしたユーザーにHTMLファイルを送るのはnginxやApacheが担うというのが一連の流れでした。
ですがNextjsの機能はこれだけではありません。Nextjsはサーバーとしての機能も有しており, npm run start
で特定のポートにアクセスするとページが現れるのはNextjsの内部でサーバーが動いているからなのです。
API Routesとは
next export
で作成された静的ファイルに含まれるJavaScriptのファイル群は全てブラウザ上で動くコードです。しかしブラウザ上ではできないこともあります。今回私が直面した問題はSendGridのAPIがブラウザから呼び出せないという問題です。これはブラウザにAPI keyが埋め込まれるとそれにアクセスしたユーザーにAPIキーが丸見えになってしまうためです。そのためSendGridのAPIをサーバー上で呼び出す必要があります。こういった場合に使われるのがAPI Routesです。
API Routesはpages/api/に作ります。呼び出す際は/api/ファイル名 でアクセスします。以下がSendGridでAPIを呼び出すサンプルコードです。
import sgMail from "@sendgrid/mail";
import { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
sgMail.setApiKey(API_KEY as string);
const msg = {
from: "foo@foo.com",
to: "bar@bar.com",
subject: `sample code`,
text: req.body.message,
};
return sgMail
.send(msg)
.then(() => {
res.status(200).end();
})
.catch(() => {
return res.status(500).end();
});
}
このコードを見てどうしてこんな回りくどい書き方をしているのかと思うかもしれません(return の後)が、async awaitで書くこともできます(たぶん)。ですが後述するVercelでデプロイする場合はこう書く必要があります。
呼び出す側は
const res = await fetch("/api/file-name", {
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});
とするだけです。
どうデプロイするか
next build && next export
で静的ファイルを作ろうとするとwarningが出ます。前述の通りAPI Routeはサーバー側の処理なのでnext exportで作成されたファイルはAPI Routeの処理を含んでいません。ですがエラーにはならないのでこのwarningを見逃すとapi呼び出しでただエラーが返ってくるだけになってしまいます。next buildで.nextディレクトリにserverディレクトリやchunk/pagesができますがこのserverディレクトリにはAPI Routesのコードが含まれています。どうやってこれらを公開しようかと調べた結果Vercelを使うという結論に至りました。
Vercelとは
Nextjsを開発したVercelが運営するクラウドホスティングサービスです。GitHubのリポジトリを選択するだけで自動的にサイトを公開してくれます。これまでfirewallの設定や証明書の取得をしてきたのが嘘のように一瞬で公開されました。しかも無料で使えるのでおすすめです。
VercelとAPI Routesを使うときの注意点
ここで先程のAPI Routesのコードが回りくどい話に戻ります。VercelでAPI Routesに非同期処理を書くとうまく動かないという状態になりました。正常に動作することもあればエラーが返ってくる場合もある、という一番困るやつです。参考記事は最後に載せていますが、どうやらサーバーが必要最低限で起動、停止を繰り返しているからではないか、と考えられます。この解決方法としてPromiseを返せばよい、ということで先程のようなコードになったのですが、async awaitで書いても同じことをしているような気もしていて、なぜこれで動いているのかあまりわかっていません。
参考文献
nextjsのapi routeをローカルでは問題が出ないのにvercelに置いた途端挙動不明になる場合
初心者でもできるNext.jsのVercelへのデプロイ方法(GitHub経由)
- Table of Contents