はじめに
日本のハワイのうちの1つ宮崎からお送りします*1。yoshinoです。
Next.jsのCSRの箇所をSSGに置き換えた時に、ローカルではエラーが起きないのに、Amplifyでデプロイした時に↓のようなエラーが生じました。また、node-fetch関連のエラーで起きていることもbacktraceには出力されていました。
ERROR: ApolloError: Expected signal to be an instanceof AbortSignal at new ApolloError
そのため、fetch + AbortControllerまわりで出ているエラーだということが考えられます。
ちなみに、AbortSignalはAbortController経由で非同期処理を中断するために利用され、fetchと組み合わせて利用されることも多いようです(詳しくはこちらを参照)。
今回は、SSGに変更したときに生じたことから、「Node.jsのバージョンがローカル(Docker image)とAmplifyで異なる 」 → 「fetch(AbortSignal)の実装が異なる」 → 「amplifyだけでエラー となる」ことが推察できました。
予想どおり、利用しているfetchのバージョンをどちらの環境で同じものを使うように変更をすることで、このエラーは解決できました(REF)。
import fetch from 'node-fetch'; import { abortableFetch } from 'abortcontroller-polyfill/dist/cjs-ponyfill'; global.fetch = abortableFetch(fetch).fetch
この記事ではNode.jsのバージョンによって、実装が異なる理由を少しだけ歴史をさかのぼって、確認してみることにします。
fetchとAbortControllerの浅い歴史
ブラウザ
今回の例はサーバーサイド(Node.js)環境の問題なので、ブラウザでは関係ありませんが、Node.jsとの比較のために確認してみます。Node.jsとブラウザのfetchやAbortControllerは対応関係はあると考えて良いと思いますが、同一のものではないことに注意してください。
fetch(Chrome: 2015-04~)
Chromeではバージョン42 (Released 2015-04-14)からサポートされています(REF)。
AbortController(Chrome: 2018-04~)
最新のすべての主要ブラウザーでAbortControllerhはサポートされています(REF)。Chromeはバージョン66 (Released 2018-04-17)からサポートされているようです。
ブラウザではfetch → AbortControllerの順序で対応されていることがわかります。
Node.js
AbortController(v15: 2021-04~)
Node.jsではv15からサポートされています(REF)。サポートされるまでの期間は、解決策の中であげているpolifyllを利用することが一般的なようです。
fetch(v18: 2023-04~)
fetchはv18からExperimentalでサポートされています。そして、このfetchではAbortControllerを利用することができます(REF)。サポートされるまでの期間はpolifyll(node-fetch)を利用して対応することが一般的です。
ブラウザとは異なり、Node.jsではAbortController → fetchの順序でサポートされています。
おわりに
以上のことから、今回のケースでは、ローカルでは最新のNode.jsのバージョンを利用しているので、AmplifyのNode.jsバージョンを最新に上げることでも、問題のエラーは解決できると思われます(まだ、試せていないのですが、ローカル環境とPROD環境のNode.jsバージョンを合わせて解決できるのであれば、それが一番良さそう)。
Node.jsのfetchの仕様は今後もバージョンにより大きく変わっていくことが予想される(v18時点では、ステータスはExperimentalですし、ブラウザのfetchの機能をすべてサポートしている訳ではない)ので、実行環境のNode.jsのバージョンとそのバージョンで何をサポートしているのかに注目すると、対応策が見えやすくなるのではないかと思います。
*1:日本にはハワイと呼ばれる箇所が複数あるようです。宮崎の海もとても綺麗なのでぜひ遊びに来てください。