Node.jsでAPIを呼び出す際の「TypeError: fetch is not a function」を修正する

beginner💚 Node.js2026-05-03| Node.js 18未満(Linux、macOS、Windows全OS対応);--experimental-fetchフラグなしのNode.js 17も対象

Error Message

TypeError: fetch is not a function
#fetch#http#node18#api#polyfill

状況

深夜2時。本番環境のバックエンドサービスがエラーを吐いている。ログを確認すると、こんな内容が目に飛び込んでくる:

TypeError: fetch is not a function
    at callExternalAPI (/app/services/api.js:12:18)
    at async processRequest (/app/handlers/request.js:34:5)

ローカルでは問題なく動いていた。同僚のマシンでも正常だ。しかも、今回はちょっとしたアップデートを push しただけ。いったい何が起きたのか?

端的に言うと、Node.js が fetch を組み込み関数として認識していない——現在の Node バージョンでは、そもそも組み込まれていないからだ。

なぜこのエラーが起きるのか

fetch はもともとブラウザの API だ。Node.js がネイティブ実装を同梱し始めたのは Node.js 18(2022年4月リリース)からで、それ以前はポリフィルなしに fetch() を呼び出すと、まさにこのエラーが発生していた。

よくある原因:

  • 本番サーバーは Node.js 14 か 16 を使っているが、開発マシンには Node.js 20 が入っている
  • ブラウザ向けチュートリアルのサンプルコードをそのまま Node.js プロジェクトに貼り付けた
  • Docker のベースイメージが想定より古い Node バージョンを使っている
  • Node.js 17 を使っているが、--experimental-fetch フラグを有効にしていない

デバッグ:まず根本原因を特定する

いきなり修正に飛びつかないこと。エラーが発生している環境で、実際に動いている Node のバージョンを確認しよう:

node --version

Docker コンテナの中で確認する場合:

docker exec -it your_container node --version

v18 未満だったら、それが原因だ。次は自分の状況に合った修正方法を選ぼう。

修正1:Node.js 18 以上にアップグレードする(推奨)

ランタイムを自由に選べる環境なら、これが最もシンプルな解決策だ。追加の依存関係も、ポリフィルも、余計なコードも一切不要。

# nvm を使う場合
nvm install 18
nvm use 18
node --version  # v18.x.x 以上と表示されるはず

Docker の場合は、ベースイメージを更新する:

# 変更前
FROM node:16-alpine

# 変更後
FROM node:18-alpine

リビルドして再デプロイすれば完了。fetch はグローバルで使えるので、import は不要だ。

動作確認:

node -e "fetch('https://jsonplaceholder.typicode.com/todos/1').then(r => r.json()).then(console.log)"

エラーではなく、ターミナルに JSON オブジェクトが出力されれば成功だ。

修正2:node-fetch を使う(古い Node から抜け出せない場合)

レガシープロジェクト、ベンダー制約、本番環境のフリーズ——Node のアップグレードが選択肢にないケースは確かにある。その場合は、ポリフィルとして node-fetch を追加しよう:

npm install node-fetch

コードでの使い方:

// CommonJS (require)
const fetch = require('node-fetch');

// ESM (import)
import fetch from 'node-fetch';

注意: node-fetch v3 以降は ESM 専用だ。プロジェクトが CommonJS(require())を使っているなら、v2 に固定しよう:

npm install node-fetch@2

API はブラウザのネイティブ fetch とほぼ同じなので、既存のコードはそのまま動くはずだ。

動作確認:

const fetch = require('node-fetch');
fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(res => res.json())
  .then(data => console.log(data));

修正3:グローバルポリフィル(一度設定して全体に適用)

ファイルごとに fetch を import するのが面倒なら、アプリのエントリーポイントでグローバルに注入する方法がある:

// index.js または app.js の先頭に記述
const fetch = require('node-fetch');
global.fetch = fetch;

// これでアプリ全体で import なしに fetch が使えるようになる

これはブラウザの動作を模倣したものだ。すでに何十ものファイルで fetch を呼び出していて、一つ一つ修正したくない場合に特に有効だ。

修正4:axios に切り替える(fetch の悩みをまるごと解消)

fetch にこだわりがないなら、axios という選択肢がある。v10 以降のすべての Node バージョンで動作し、JSON の処理も自動でやってくれるうえ、エラーメッセージも最初からわかりやすい:

npm install axios
const axios = require('axios');

const response = await axios.get('https://api.example.com/data');
console.log(response.data);

移行は簡単で、fetch(url).then(r => r.json())axios.get(url).then(r => r.data) に置き換えるだけ。たいていの場合、差分はそれだけで済む。

修正5:Node.js 17 — 実験的 fetch を有効にする

Node 17 は特殊なケースだ。fetch は存在するが、フラグで隠されている:

node --experimental-fetch your-script.js

package.json で常時有効にするには:

{
  "scripts": {
    "start": "node --experimental-fetch index.js"
  }
}

これはあくまで一時的な回避策であり、根本的な解決策ではない。Node 17 は2022年6月にサポートが終了しているので、素直に18以上へアップグレードしよう。

環境の不一致という落とし穴

このバグには厄介なパターンがある——手元では問題なく動くのに、CI や本番環境に上げた瞬間に壊れるというケースだ。これはほぼ確実に、環境間での Node バージョンのズレが原因だ。

Node のバージョンを明示的に固定しよう。.nvmrc ファイルを追加する:

echo "18" > .nvmrc

または package.jsonengines フィールドを設定する:

{
  "engines": {
    "node": ">=18.0.0"
  }
}

GitHub Actions の場合は、このファイルを参照するよう setup-node を設定する:

- uses: actions/setup-node@v3
  with:
    node-version-file: '.nvmrc'

こうすれば、ローカル・CI・本番環境がすべて同じバージョンで動くようになる。深夜のサプライズはもうおしまいだ。

まとめ

  • Node のバージョンを固定すること。 .nvmrc、package.json の engines フィールド、明示的な Docker イメージタグを使おう——node:latest は絶対に使わない。
  • ブラウザの API は Node にデフォルトで存在しない。 fetchlocalStoragewindow——これらは自分で追加しない限り、Node 環境では使えない。
  • Docker のベースイメージに注意する。 バージョンタグなしの node:alpine は、イメージを pull するタイミングによって異なる Node バージョンを引っ張ってくることがある。
  • 環境を揃えること。 本番が Node 16 で動いているなら、ローカルの開発環境も 16 にすべきだ——20 ではなく。

Related Error Notes