Next.js の api を使って非同期処理のモックを作っていて Fetch API だとステータスコードが 200 系以外でもエラーが発生しないみたいだったのでステータスコードでエラーハンドリングできるようにしたメモ
環境
- node.js
v18.14.0
- Next.js
13.2.4
- React
18.2.0
- React
- TypeScript
5.0.2
API (Next.js)
Next.js でシンプルな API を作成
- POST 以外の場合は 405 (
Method Not Allowed
) を返す - 認証が通らなかったら 400 (
Bad Request
) を返す - 認証が通ったら 200 で
{"success": "true"}
の json を返す
/src/api/auth.ts
import { NextApiRequest, NextApiResponse } from 'next'; type Data = { success: boolean; message?: string; }; export default function handler( req: NextApiRequest, res: NextApiResponse<Data>, ) { if (req.method !== 'POST') { return res.status(405).send({ message: 'Bad Request!', success: false }); } const { user, password } = req.body; if ( !login({user, password}) ) { return res.status(400).send({ message: 'Bad Request!', success: false }); } return res.status(200).json({ success: true }); }
フロントからの Fetch
const postLoginForm = async ({ user, password }) => { const method = 'POST'; const headers = { Accept: 'application/json', 'Content-Type': 'application/json', }; const body = JSON.stringify({ user, password }); try { const res = await fetch('/api/auth', { method, headers, body }); const { success } = await res.json(); return !!success; } catch (error) { if (error instanceof Error) { console.log('Error', error.message); } else { console.log('Error'); } // エラー時の処理 } return false; }
上記のコードは API からステータスコードが 405 の時もログインに失敗して 400 が返ってきた場合も処理が cactch 節に流れません
Response.ok
を使ってステータスコードが 200 番台以外は例外を発生させるようにする
Fetch API は通信そのものがエラーにならない限りステータスコードが 200番台以外でも例外を発生させないようなので、try - catch
を使ったエラーハンドリングをするには意図的に例外を発生させる必要がありました
Response.ok
(読取専用)
レスポンスが成功 (200–299 の範囲のステータス) したか否かを通知する論理値が入ります。
cf. Response.ok - Web API | MDN
Fetch API の返すレスポンスオブジェクトの Response.ok
が false
なら例外を発生させれば OK
const postLoginForm = async ({ user, password }) => { // 略 try { const res = await fetch('/api/auth', { method, headers, body }) + .then((res) => { + if ( !res.ok ) { + throw new Error(res.statusText); + } + return res; + }); const { success } = await res.json(); return !!success; } catch (error) { if (error instanceof Error) { console.log('Error', error.message); } else { console.log('Error'); } // エラー時の処理 } return false; }
レスポンスのステータスが 200 番台以外だと例外にできるようになりました
このとき error.message
には Response.statusText
が入っています。これは API から返されるデータの message ではなくBad Request
とか Internal Server Error
のようなステータスコードに対応したテキストになります。
API から返されるメッセージを error.message
にしたい場合は Response から json を取り出して意図的に throw する必要があります
カスタムメッセージを使う場合
const postLoginForm = async ({ user, password }) => { // 略 try { - const res = await fetch('/api/auth', { method, headers, body }) - .then((res) => { - if ( !res.ok ) { - throw new Error(res.statusText); - } - return res; - }); - const { success } = await res.json(); + const data = await fetch('/api/auth', { method, headers, body }) + .then( async(res) => { + const data = await res.json(); + if ( !res.ok ) { + throw new Error(data.message); + } + return data; + }); + const { success } = data; return !!success; } catch (error) { if (error instanceof Error) { console.log('Error', error.message); } else { console.log('Error'); } // エラー時の処理 } return false; }
ステータスコードに関わらず response.json()
で API から渡されたデータを取り出しその中のメッセージを threw すれば OK
所管
axios や ky を使うことが多く fetch をガッツリ使ってこなかったのでステータスコードが 200 以外でもエラーとして扱われないのは予想外だったので Response.ok
や Response.statusText
など理解が深まってよかったです!
おわり₍ ᐢ. ̫ .ᐢ ₎
[参考]