HB DevTools

Knowledge

Cloudflare Pages の Edge Runtime で Node.js 依存ライブラリが動作しない問題

cheerio など Node.js API に依存するライブラリが Cloudflare Workers の Edge Runtime で動作しない原因と、pure JavaScript 製ライブラリへの移行手順を解説します。

CloudflareEdge Runtimeデバッグ

1. 発生した現象

SEO Analyzer ツールで他ドメインの URL を解析しようとすると、ローカル開発環境(npm run dev)では正常に動作するにも関わらず、 Cloudflare Pages にデプロイした本番環境では以下のエラーメッセージが表示されて解析が一切できない状態が発生しました。

解析中にエラーが発生しました。しばらくしてから再試行してください。

このメッセージはクライアントコンポーネント(SeoAnalyzerClient.tsx)のcatch ブロックから表示されます。 つまり、Server Action(analyzeUrl)が値を返すのではなく、例外を throw していることを意味します。 Server Action 内部には包括的なエラーハンドリングが実装されていたため、 「なぜ throw されるのか」の特定に調査が必要でした。

2. 原因の特定

原因は HTML パーサーとして使用していた cheeriocheerio/slim)が Cloudflare Workers の Edge Runtime 上で動作しないことでした。

cheerio は内部で HTML パーサーとして htmlparser2 を使用しており、htmlparser2 は Node.js の Buffer やストリーム(Node.js Streams)といった Node.js 固有の API に依存しています。 これらの API は Cloudflare Workers の実行環境には存在しないため、cheerio.load(html) を呼び出した時点でランタイムエラーが発生します。

Server Action の実装は以下のような構造でした。

  • 外部 fetch のエラー → try-catch で捕捉し、エラーオブジェクトを返す(正常)
  • analyzeSeo(url, html) の呼び出し → try-catch で囲まれているが、 その内部で cheerio.load() が throw するため、catch して エラーオブジェクトを返すはずが…
  • 実際には、Cloudflare Workers 環境ではモジュールの初期化段階import 時)または 初回呼び出し時に Buffer 未定義エラーが発生し、 Server Action 全体が応答不能になってクライアントに例外が伝播していた

3. なぜローカルでは動くのか

ローカル開発環境(npm run dev)では Next.js は Node.js ランタイムで動作します。 Node.js には Buffer やストリームが標準搭載されているため、cheerio は問題なく動作します。

一方、Cloudflare Pages にデプロイした場合、@cloudflare/next-on-pages によってすべてのルート(Server Actions を含む)がCloudflare Workers(Edge Runtime)上で動作するように変換されます。 Edge Runtime は V8 アイソレート上に構築されており、Node.js API は原則として利用できません。

この「ローカルは Node.js / 本番は Edge Runtime」という環境差異が、 ローカルのみで動作確認した際に問題を見逃す原因となります。

4. 解決策:pure JavaScript 製ライブラリへ移行

cheerio を、Node.js API に依存しない pure JavaScript 製の HTML パーサーnode-html-parser に置き換えることで解決しました。node-html-parser は文字列操作と正規表現のみで実装されており、 Cloudflare Workers を含むあらゆる JavaScript 実行環境で動作します。

API の対応関係は以下のとおりです。

  • import * as cheerio from 'cheerio/slim' import { parse } from 'node-html-parser'
  • const $ = cheerio.load(html) const root = parse(html)
  • $('title').first().text() root.querySelector('title')?.textContent
  • $('meta[name="description"]').attr('content') root.querySelector('meta[name="description"]')?.getAttribute('content')
  • $('h1, h2, h3').each((_, el) => { $(el).text() } ) root.querySelectorAll('h1, h2, h3').forEach(el => { el.textContent } )

移行後、既存の 41 件の単体テストおよび 6 件の E2E テストがすべて変更なしで通過し、 動作の互換性が確認できました。

5. 今後のための判断基準

Cloudflare Pages(Edge Runtime)でライブラリを使用する際は、以下の観点で確認します。

  • Node.js API への依存を確認する: ライブラリが Bufferstreamfspathcrypto(Node.js 版)などを使っていれば Edge Runtime では動作しません。npm のリポジトリや README に "Node.js only" の記載がある場合も同様です。
  • 「純粋JavaScript」かどうかを確認する: 文字列操作・正規表現・Web API(fetchURLTextEncoder 等)のみで 実装されたライブラリは Edge Runtime でも動作します。
  • Cloudflare Workers の互換性リストを参照する:nodejs_compat フラグ(wrangler.toml compatibility_flags)を 有効にすると Buffer など一部の Node.js API が polyfill されますが、 すべての API が揃うわけではなく、コード変更なしで解決できるとは限りません。 根本的な解決として pure JavaScript 実装への移行を優先してください。
  • 本プロジェクトの実績:node-html-parser(HTML パース)は Edge Runtime 対応の確認済み実装です。 新たに HTML を扱う処理を追加する場合はこれを使用してください。