🍆

Next.js + Supabase + Cloudflare PagesでWebサービスを開発・デプロイする際に起きた問題と感想

2024/05/04に公開

表題の通り、モダンな構成で個人開発としてWebサービスを開発し、Cloudflare Pagesにデプロイしました。

開発した際に発生した問題と感想を共有しておきます。

※Next.jsはApp Routerを使っています。

なぜこの構成で開発したのか?

▶ Next.js
説明割愛します。
もはや開発の標準フレームワークすぎて説明不要ですね!

▶ Supabase
個人開発レベルであれば無料で使えるからです。
そして、まるでバックエンドが存在しないかのように(感覚的には)直接的にDBにCRUDできるし、認証機能も備わっていて便利だからです。
利益が出てから有料化すれば十分に間に合うので金銭的にはリスクゼロで運営できるのは最高な環境ですね!

▶ Cloudflare Pages
こちらも同様に個人開発レベルであれば無料で使えるからです。
また、デプロイも簡単ですし、(ステージング的な意味合いを持つ)プレビュー機能が使えるのも嬉しいところです。有料化したとしても月5ドル程度で収まりそうです。Cloudflareはマジで慈善団体!

つまり、0円でノーリスクなのに爆速なWebサービスが公開できてしまいます。世界中の投資家さんに感謝しながら有り難く使わせて頂きます!

※ドメイン代もA8.netなどでセルフバックすれば実質無料で行けます!(セルフバック忘れがちだけど活用していきましょう!)

0. どんなサービスを作ったのか?

  • スクレイピングでデータを収集している
  • 加工したデータを元にコンテンツを構成している
  • Next.jsのSSRを利用している
  • 500,000ページ以上ある

というものです。
ローカル上で集めたデータを公開しているサービスです。

※サービス名は伏せさせて頂きます。

1. Supabase CLIが便利だった

よくこの構成で開発する場合、「Prisma」が使われる傾向にあると思います。
しかし、SupabaseにおいてはPrismaは不要だと思っています。というのも、Supabase CLIがすごく良い感じで、GUIで作成したテーブルなどもマイグレーションファイルに変換できる機能が備わっています。

かなり前の経験なので今はどうなっているか未確認なので聞き流して頂きたいのですが、Prismaを使うことによってSupabaseの不具合に遭遇することが多くありました。この経験からPrismaを使う気には一切なりませんでした。

Supabase CLIだけでかなりスムーズな開発ができます。

▼ PrismaについてSupabaseの中の人「タイラーさん」による見解

https://twitter.com/dshukertjrjp/status/1771725005144908077

2. Windows環境だとちょこちょこ問題に遭遇する。(すぐに解決できるレベル)

僕はWindows 11を使っています。
WindowsとSupabaseの相性が若干悪いようでWindows特有の問題に遭遇することがありました。

2-1. TypeScript型の文字コードが不適切だった

supabase gen types typescript で生成した型データの文字コードが不適切で、 npm run build 時にエラーが発生してしまう問題がありました。文字コードを UTF-8 に直すだけで解決します。手作業で都度直すのは面倒だったので prebuild のタイミングで自動変換する仕組みを実装して意識することなくビルドできる環境を手に入れました。

▼解決策の記事を書きました。
https://zenn.dev/masa5714/articles/8303ae09d4e09d

本番環境の Supabase と連携する際に supabase link --ref-project というコマンドを叩くのですが、このコマンドが動いてくれない事象が発生しました。

https://github.com/supabase/supabase/issues/15184

こちらに投稿されている方法で、

$env:SUPABASE_DB_PASSWORD="【ここにDBパスワード】"; supabase link --project-ref 【ここにプロジェクトのref値】

このコマンドで解決できました。

2-3. ローカルでSupabaseを動かすとめちゃくちゃ重かった

これはSupabaseが問題というよりはDocker側の話かもしれませんが、とにかくローカル環境が重かったです。メモリ使用率が99%となり何も作業できないレベルでした。

簡単な設定変更だけで解決しました。

https://zenn.dev/masa5714/articles/3b5ea07c15f159

3. メタデータの出力にも工夫が必要だった

これはNext.js寄りのお話になるのですが、メタデータ出力とブラウザ表示で分けて処理されるため、Supabaseへデータ取得が2回リクエストが行われてしまう問題がありました。

コードで見る(こんな感じ)
page.tsx
// 動的にメタデータを出力する
export async function generateMetadata({ params }: { params: { id: string } }) {
  const data = await getPostData(params.id);

  return {
    title: data.title,
    description: data.description
  };
}

// ブラウザ画面に出力する
export default async function MyPage({ params }: { params: { id: string } }) {
  const data = await getPostData(params.id);

  return (
    <>
      <p>{data.title}</p>
    </>
  );
}

どちらも同じデータを使っているのに無駄にリクエストが2回発生しているので削減する必要がありました。そこで import { cache } from "react"; を活用した実装を行いました。

これでキャッシュが効いてくれて無駄なリクエストが発生しなくなりました。

解決策の詳しい内容は下記の記事をご覧ください。
https://zenn.dev/masa5714/articles/f28b7ec738bf77

4. Cloudflare PagesにデプロイするにはEdge Runtimeを有効にする必要があり、やや修正が必要だった

Next.jsをCloudflare Pagesにデプロイするには Edge Runtime を有効にしなければなりません。それに伴う修正がいくつか必要だったので共有します。

4-1. SSRするページに export const runtime = "edge"; をつける

Next.jsでは page.tsxに export const runtime = "edge"; を追記するだけで Edge runtimeが有効になります。SSRを使ってるページではこの記述を追加するだけで解決します。

4-2. サイトマップの生成処理が動かなかった

App Routerにはサイトマップ生成機能が備わっていますが、これが Edge Runtimeで動かなかったため使えませんでした。

今回のサービスでは50万ページ以上あるためサイトマップを分割しなければなりません。(サイトマップは5万個のURLしか含められないため。)また、分割出力したサイトマップのインデックスを出力してくれる仕組みも無いため辛かったです。

ということで、かなり面倒ですがサイトマップを生成する仕組みを独自実装して解決しました。

▼catnoseさんの下記記事の「3. xmlのコードを生成する」箇所をベースに構築しました。
https://zenn.dev/catnose99/articles/c441954a987c24

サイトマップだけで8時間ぐらい溶かした...。(サイトマップの準備は意外と時間かかるかもなので注意してください!)

5. SupabaseのGUI上でCSVインポートを行ったら文字化けしてしまった

SupabaseのGUI(Supabase Studio)には Import data via spreadsheet というCSVファイルからテーブルを作成する便利な機能があります。

しかし、これを使うのはオススメできません。

というのも、43万行ぐらいのデータをインポートしたところ、200件強で文字化けが発生してしまいました。不完全なデータを挿入してしまうことになるので極力使わない方がいいかもしれません。

DBeaverを使ってCSVインポートを行ったところ正常に挿入できました。

https://zenn.dev/masa5714/articles/f2ad6883a1bd3d

6. データ移行はDBeaverを使った

本番環境時にはローカル環境で溜め込んだデータを本番環境に移行しなければなりません。Supabase CLIにはそのような仕組みがありませんので、DBeaverを使って移行させました。( supabase db push --linked はマイグレーションファイルの実行のみです。 )

7. データベース使用量は290MBぐらい

Supabaseの無料枠ではデータベース使用量500MBという制限があります。データベースを普段触らない人にはこの規模感が分かりづらいかと思いますので、今回のケースの使用量を共有します。

テーブル数: 4つ
データ量: 約112万レコード(行)
インデックス: 結構張ってる(なんて伝えるのが適切か分からない...。)

1レコードあたりは6カラム程度なのでデータが大きくならずに済んだ部分はありますが、500MBのデータベースは個人開発レベルでは十分すぎるとお考え頂いてよろしいかと思います。

ちなみに僕はSupabaseの課金に到達しにくくするためにStorage機能を使っていません。画像などはGCPのCloud Storageを使っています。ですので、文字列の転送だけで5GBに到達しなければ無料で使えるということですね!

8. ページ数が多すぎてCloudflare Pagesに課金しないとダメそう

完全無料で運用できると考えていましたが、サイトマップに含まれるページ数が多すぎてCloudflare Pageに課金をしないとダメそうです。なぜなら、Googleボットなどがクロールのために大量にリクエストをしてくるからです。

今日だけでも9万リクエストをしてきており、1日上限の10万リクエストに到達しそうです。様子を見て課金をしようと思います。ただ、GCPのCloud Runと比べればかなり安く素晴らしいサーバーを借りられるので全然問題ないですね。

※Supabaseについては文字列が配信されてるだけなので無料枠で済みそうです。

【付録1】個人開発ではPC版を作らない方針にしました

デザインが苦手な僕はPC版のサイトを準備しないことにしました。スマホだけでデザイン辛いのにPCも作るのは無理です。それとリサイズ周りで考慮すべきことが多すぎる割にリターンが無さすぎます。(僕のサイトで計測した限りでは8割以上がスマホユーザーなので...。たった2割のためにスマホのUIのクオリティを下げるのは不適切だと感じています。)

ただし、準備しませんが、一応PCでも見れる状態にはしています。
※横スクロールが必要な要素はドラッグでスクロールできるようにはしています。最低限の考慮だけで済ませられます。

▼ 下記の手法を用いてスマホサイトに対応しています。(どんなデバイスでも崩れにくい。現時点で最強の手法だと考えています。)
https://zenn.dev/masa5714/articles/0a05659cf6e84b

【付録2】Cloudflare Pageの無料枠はリクエスト上限を超えても直ちにストップすることはない

上記は1日あたりのリクエスト数です。
これほど膨れ上げがっている原因は、ページ数が多い影響でGoogleクローラが大量にリクエストしてきているためです。このように約5万リクエストを超過してしまっていますが、それでもリクエスト制限が直ちにかかるということは無いようです。Cloudflareさん神すぎる。

2024年5月8日: とはいえ、迷惑かけ続けるのも良くないので課金しました。(なぜか請求が4.79ドル🤔)

さいごに

細かいところで困ることがいくつかありましたが、開発体験自体は素晴らしいものでした!
そして何より Supabaseも無料枠で使えるし、Cloudflare Pagesも無料枠で使えるし、Webサービスの公開が無料でできる夢のような環境が嬉しかったです!

気軽に開発もできて、公開もできるので、ぜひこの構成で開発してみてくださいね。

Discussion