VercelにNext.jsをデプロイしたときに「TypeError: fetch failed」がでて
デプロイに失敗しました。
解決していきまーーーす!
TypeError: fetch failed ①
エラー内容確認
Vercelでデプロイログを確認しました
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
TypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11576:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { cause: Error: connect ECONNREFUSED 127.0.0.1:3000 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1495:16) at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 3000 } } Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error TypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11576:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) |
fetchでRoute Handlersで作成したAPIを呼び出したときにエラーになってるっぽい
ググってみる
「TypeError: fetch failed」「ECONNREFUSED」ここら辺の単語でググってみました
↑の記事によると、
Node.js 17/18 ではlocalhostがIPv6で名前解決されることが原因みたいです。
Vercelで Project Settings > General > Node.js Version を確認してみると、
確かに18系っぽい。。
ただ、デプロイログを再度見てみると127.0.0.1で解決できているので原因は別にありそう
まあ、一応試してみます。
コード修正
たたいているAPIのURLをlocalhost ⇒ 127.0.0.1へ変更しIPv4を直書きしてみます
1 2 3 4 5 6 |
// 変更前 const url = "http://localhost:3000/api/user"; ↓ // 変更後 const url = "http://127.0.0.1:3000/api/user"; |
再デプロイ
修正したコードをGitHubにpushして再デプロイしてみます。
失敗しました。。。
デプロイログを見てみると、エラーの内容変わらずでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
TypeError: fetch failed at Object.fetch (node:internal/deps/undici/undici:11576:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { cause: Error: connect ECONNREFUSED 127.0.0.1:3000 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1495:16) at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 3000 } } |
うーーん、なんでしょう??
TypeError: fetch failed ②
エラー内容確認
fetch failedなので、やっぱりAPIがたたけないことが原因ですよね。。
localhostとか127.0.0.1ではなく、Vercelのデプロイ用のURLじゃないとダメなのか??
試してみます
Vercelのデプロイ用のURLとは?
Vercelでデプロイしたときに発行されるURLのこと。GitでいうコミットIDぐらいの感覚。
この値はVercelで設定されているVERCEL_URLという環境変数から取得できます
Vercelで設定されている環境変数については↓をご覧ください
今回はClient Componentでもこの環境変数を使用したいので、
NEXT_PUBLIC_VERCEL_URL を使います
コード修正
APIのURLを環境変数から生成するようにします
(変更前)
1 2 3 4 5 6 |
// APIのURL const url = "http://localhost:3000/api/user"; // APIへリクエスト const res = await fetch(url, { cache: "no-store", }); |
(変更後)
- .env ファイルにNEXT_PUBLIC_API_PREFIX と NEXT_PUBLIC_VERCEL_URLを定義
- 環境変数を読み込む src/lib/config.tsを作成
- APIのURLをconfig.tsファイルを読み込み生成
という3つのステップで修正します。
1..env ファイルにNEXT_PUBLIC_API_PREFIX と NEXT_PUBLIC_VERCEL_URLを定義
1 2 |
NEXT_PUBLIC_API_PREFIX="http://" NEXT_PUBLIC_VERCEL_URL="localhost:3000" |
2.環境変数を読み込む src/lib/config.tsを作成
1 2 3 4 |
export const config = { apiPrefix: process.env.NEXT_PUBLIC_API_PREFIX ?? "http://", apiHost: process.env.NEXT_PUBLIC_VERCEL_URL ?? "localhost:3000", }; |
3.APIのURLをconfig.tsファイルを読み込み生成
1 2 3 4 5 6 7 8 |
import { config } from "@/lib/config"; // APIのURL const url = config.apiPrefix + config.apiHost + "/api/user"; // APIへリクエスト const res = await fetch(url, { cache: "no-store", }); |
ビルド設定修正
NEXT_PUBLIC_VERCEL_URLはVercelで設定されているのですが
NEXT_PUBLIC_API_PREFIXはあらかじめ設定しておく必要があります
Project Settings > Environment Variables にて
Key :NEXT_PUBLIC_API_PREFIX
Value :https://
で環境変数を設定します
再デプロイ
修正したコードをGitHubにpushして再デプロイしてみます。
失敗しました。。。
デプロイログを見てみると、エラーの内容変わりました。
1 2 3 4 5 6 7 8 9 |
SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>) at parseJSONFromBytes (node:internal/deps/undici/undici:6662:19) at successSteps (node:internal/deps/undici/undici:6636:27) at node:internal/deps/undici/undici:1236:60 at node:internal/process/task_queues:140:7 at AsyncResource.runInAsyncScope (node:async_hooks:203:9) at AsyncResource.runMicrotask (node:internal/process/task_queues:137:8) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) |
SyntaxError: Unexpected token < in JSON at position 0
エラー内容確認
1 2 3 4 5 6 7 8 9 |
SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>) at parseJSONFromBytes (node:internal/deps/undici/undici:6662:19) at successSteps (node:internal/deps/undici/undici:6636:27) at node:internal/deps/undici/undici:1236:60 at node:internal/process/task_queues:140:7 at AsyncResource.runInAsyncScope (node:async_hooks:203:9) at AsyncResource.runMicrotask (node:internal/process/task_queues:137:8) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) |
「JSONだと思ったけど1文字目が<だから、JSONじゃなくない??」
というエラーです。
1文字目が<のため、おそらくAPIからHTMLが返ってきていると思います。
APIからのレスポンスヘッダーを確認する
↓のようにヘッダを出力させます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import { config } from "@/lib/config"; // APIのURL const url = config.apiPrefix + config.apiHost + "/api/user"; // APIへリクエスト const res = await fetch(url, { cache: "no-store", }); // レスポンスヘッダを出力する console.log(res.headers); // レスポンスボディを取り出す const data = await res.json(); |
このコードでデプロイしてみるとログがこんな感じでした
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
HeadersList { cookies: null, [Symbol(headers map)]: Map(23) { 'cache-control' => { name: 'Cache-Control', value: 'public, max-age=0, must-revalidate' }, 'connection' => { name: 'Connection', value: 'keep-alive' }, 'content-encoding' => { name: 'Content-Encoding', value: 'br' }, 'content-security-policy' => { name: 'Content-Security-Policy', value: "default-src 'self' vercel.com *.vercel.com vercel.live instant-preview-site.vercel.app instant-preview-site-bc8byjwcq.vercel.sh;script-src 'self' 'unsafe-eval' 'unsafe-inline' va.vercel-scripts.com vercel.com *.vercel.com vercel.live instant-preview-site.vercel.app instant-preview-site-bc8byjwcq.vercel.sh;style-src 'self' 'unsafe-inline' vercel.com *.vercel.com vercel.live instant-preview-site.vercel.app instant-preview-site-bc8byjwcq.vercel.sh;font-src 'self' vercel.com *.vercel.com vercel.live instant-preview-site.vercel.app instant-preview-site-bc8byjwcq.vercel.sh *.gstatic.com;connect-src data: *;" }, 'content-type' => { name: 'Content-Type', value: 'text/html; charset=utf-8' }, 'date' => { name: 'Date', value: 'Sat, 16 Sep 2023 14:56:46 GMT' }, 'feature-policy' => { name: 'Feature-Policy', value: "fullscreen 'self'; camera 'none'" }, 'referrer-policy' => { name: 'Referrer-Policy', value: 'origin-when-cross-origin' }, 'server' => { name: 'Server', value: 'Vercel' }, 'strict-transport-security' => { name: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains; preload' }, 'vary' => { name: 'Vary', value: 'RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url' }, 'x-content-type-options' => { name: 'X-Content-Type-Options', value: 'nosniff' }, 'x-dns-prefetch-control' => { name: 'X-Dns-Prefetch-Control', value: 'on' }, 'x-download-options' => { name: 'X-Download-Options', value: 'noopen' }, 'x-edge-runtime' => { name: 'X-Edge-Runtime', value: '1' }, 'x-frame-options' => { name: 'X-Frame-Options', value: 'DENY' }, 'x-matched-path' => { name: 'X-Matched-Path', value: '/[[...slug]]' }, 'x-powered-by' => { name: 'X-Powered-By', value: 'Next.js' }, 'x-robots-tag' => { name: 'X-Robots-Tag', value: 'noindex' }, 'x-vercel-cache' => { name: 'X-Vercel-Cache', value: 'MISS' }, 'x-vercel-id' => { name: 'X-Vercel-Id', value: 'iad1:iad1:iad1::7zktw-1694876206302-13dbeb12bf20' }, 'x-xss-protection' => { name: 'X-Xss-Protection', value: '0' }, 'transfer-encoding' => { name: 'Transfer-Encoding', value: 'chunked' } }, [Symbol(headers map sorted)]: null } |
‘content-type’ => { name: ‘Content-Type’, value: ‘text/html; charset=utf-8’ },
なので、やっぱりHTMLが返ってきていますね。
コード修正
どうしてHTMLが返ってきているのかは不明だったので、
レスポンスボディを取り出す処理をtry-catchしていきます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import { config } from "@/lib/config"; // APIのURL const url = config.apiPrefix + config.apiHost + "/api/user"; // APIへリクエスト const res = await fetch(url, { cache: "no-store", }); // レスポンスボディを取り出す try { const data = await res.json(); } catch(error) { condsole.log(error) } |
再デプロイ
修正したコードをGitHubにpushして再デプロイしてみます。
成功しました!!
最後に
今回はとりあえずtry-catchで逃げましたが、
APIからHTMLが返ってきている原因を探らないとですね。。
コメント