🔥

Cloudflare PagesでNext.jsとHonoを動かす

2023/10/02に公開

はじめに

おはようございます、加藤です。先日 Serverless Days 2023 Tokyo に参加してきました。2日目はCloudflare Workers & Hono Workshopに参加し、Hono便利だなーってなったので知識を定着させる目的で環境を構築してみます。

前提

node --version
v20.6.1
npm --version
9.8.1

Wrangler CLIでログインが済んでいない場合は下記のコマンドでログインしてください。

npx wrangler login

Next.jsのセットアップ

プロジェクトを作成します。

npm create cloudflare@latest tmp -- --framework=next

> Need to install the following packages:
> create-next-app@13.4.19
> Ok to proceed? (y) y
> ✔ Would you like to use TypeScript? … Yes
> ✔ Would you like to use ESLint? … Yes
> ✔ Would you like to use Tailwind CSS? … No
> ✔ Would you like to use `src/` directory? … No
> ✔ Would you like to use App Router? (recommended) … Yes
> ✔ Would you like to customize the default import alias? … No

> Do you want to use the next-on-pages eslint-plugin? … Yes

> Do you want to deploy your application? … No

ディレクトリtmp内のファイルを全てプロジェクトルートへ移動します。ドットファイルの移動を忘れないように注意してください。

mv tmp/* ./
mv tmp/.eslintrc.json tmp/.git tmp/.gitignore ./
rmdir tmp

NPMプロジェクト名がtmpになっているので変更したい場合はpackage.jsonnameを変更してください。nameはロックファイルにも含まれるので変更後はnpm installで再生成する必要があります。

初回デプロイを行います。今回は新規プロジェクトを作成しました。${{YOUR_PROJECT_NAME}}は自由な名前に置換してください。

npm run pages:deploy

> No project selected. Would you like to create one or use an existing project?
> ❯ Create a new project
>   Use an existing project
> ✔ Enter the name of your new project: … ${{YOUR_PROJECT_NAME}}
> ✔ Enter the production branch name: … main
> ✨ Successfully created the '${{YOUR_PROJECT_NAME}}' project.

WebブラウザでCloudflareのダッシュボードを開き、作成したPagesのプロジェクトに対して2つの設定を行います。最初wrangler.tomlを書けば良いのかなと思いましたが、Cloudflare Pagesはwrangler.tomlには対応していませんでした。

  1. 環境変数NODE_VERSION = 20を設定する
  2. Runtime Featuresのcompatibility flag nodejs_compatを設定する

どちらもProductionとPreviewそれぞれ設定しておきます。

参考元: https://zenn.dev/microcms/articles/1b4331eca6e512#githubリポジトリとの連携

一旦apiディレクトリを削除します。

rm -rf app/api

再度デプロイします。

npm run pages:deploy

CLIに表示されたURLへアクセスしNext.jsのWelcomeページが表示されることを確認します。

Honoのセットアップ

Honoとクライアントとして使用するSWRをインストールします。

npm install hono swr

/api配下への全てのリクエストをHonoで処理するエンドポイントを作成します。

mkdir -p app/api/[[...slug]]
touch app/api/[[...slug]]/route.ts

app/api/[[...slug]]/route.ts

import { Hono } from "hono";

export const runtime = "edge";

const app = new Hono().basePath("/api");

app.get("/hello", (c) => c.text("Hello World!"));
app.post("/hello", (c) => c.json({ message: "Hello World!" }));

export const GET = app.fetch;
export const POST = app.fetch;

app/page.tsxを編集して作成したAPIから取得した値を表示します。

app/page.tsx

"use client";

import useSWR from "swr";

export default function Home() {
  const { data: text } = useSWR("/api/hello", (url) =>
    fetch(url).then((res) => res.text())
  );
  const { data: json } = useSWR("/api/hello", (url) =>
    fetch(url, { method: "POST" }).then((res) => res.json())
  );

  return (
    <main>
      <p>text: {text}</p>
      <p>json: {JSON.stringify(json)}</p>
    </main>
  );
}

デプロイしてともにHello World!が表示されれば完成です。

あとがき

少量の設定で簡単にNext.jsとAPIを作成することができました。
今回はHonoをCloudflare WorkersではなくCloudflare PagesのFunctionsで動かしました、これは汎用的なAPIではなくPagesにデプロイするフロントエンド専用のAPI(BFF的な感じ)の用途をイメージして構築しました。
具体的には

  • Cookieセッションで認証した後セッションからユーザーIDや認可の為のJWTを取り出してからバックエンドAPIへリクエストを行う
  • バックエンドAPIからのレスポンスをユーザーが理解しやすいものに置き換える
  • バックエンドAPIからのレスポンスをセキュリティ要件のために隠蔽する
    といった用途を想定しています。
    HonoにはRPC機能があり、これを使うことでブラウザからAPIへのリクエストを型の支援を受けながら記述することもできます。まさにフロントエンドからはRPCとして呼び出す感覚なのでマッチしそうです。

Pages Functionsで動かすと同一ドメインなのでCORSやCookieのDomain属性の考慮が不要という利点があります。もし、複数のクライアントが存在するAPIとして構築するなら、別途Workersで構築するのが好ましいでしょう。

誤りやより良い方法などがあればコメント頂けるとありがたいです。

参考元

東急URBAN HACKS

Discussion