1. ある商品はそのまま返る
  2. 404は理由つきで統一形式
  3. 500は中身を出さず決まった文言
コース一覧
Express道場 fleama開発室のIssue10件
#107 エラーの形式がバラバラ

Express道場 fleama開発室のIssue10件

Node.js入門で作り上げた fleama API の開発室に、スプリントの助っ人として参加する演習専用コースです。解説はありません。1問が1件の Issue で、報告者は商店街の住人たち。依頼と完成条件だけを読み、白紙から自分で組み立てます。手が止まったときのために、方針・使う構文・部分解の3段階のヒントを各問に用意しています。ヘルスチェックから始まり、一覧API、404の扱い、並び替えとページング、入力検証、自作ミドルウェア、エラー形式の統一、所有者チェック、二重販売を止めるトランザクションを経て、最後はいいね機能をエンドポイント設計から任されるところまで、10件の Issue で Express の主要な道具を一巡します。1問15分から45分、全10問で約5時間です。Node.js入門を終えて「作れたけれど実務で通用するか不安」と感じている方に向いています。

1
今週のスプリント
01. #101 監視用ヘルスチェックが欲しい15分
02. #102 カテゴリ一覧APIが欲しい20分
03. #103 消えた商品を開くと落ちる20分
04. #104 検索に並び替えとページングを25分
05. #105 0円出品が通ってしまう25分
06. #106 調査用のアクセスログを25分
07. #107 エラーの形式がバラバラ30分
08. #108 他人の出品を値下げできてしまう30分
09. #109 同じ商品が2人に売れた35分
10. #110 新機能「いいね」を設計から45分

#107 エラーの形式がバラバラ

中段 / 目安 30分


Issue #107 エラーの形式がバラバラ

報告者: 凛(テックリード)

「404 は {"error":...}、500 は素のスタックトレース、検証エラーは配列……クライアント担当が泣いています。エラー応答の形式を1つに統一してください」


受け取る側の身になると、この苦情は当然です。エラーのたびに形が違えば、形ごとに読み分けるコードを書くことになります。しかも新しいエラーが増えるたびに、その対応も増えます。

統一するには、エラーを返す場所を1か所に集めます。各ルートは「失敗した」とだけ伝え、どう返すかは1か所が決める形です。

伝え方が next(err) です。

JavaScript

const err = new Error("商品が見つかりません"); err.status = 404; return next(err);

引数なしの next() が「次へ進む」なのに対し、next(err) は「失敗したので受け皿へ」という意味になります。ステータスコードはエラー自身に持たせておきます。

受け皿がエラー用のミドルウェアです。

JavaScript

app.use((err, req, res, next) => { res.status(err.status || 500).json({ error: err.message }); });

引数が4つあるのが目印です。3つだと普通のミドルウェアとして扱われ、エラーを受け止められません。省略もできません。

置く場所はすべてのルートより後です。リクエストは上から順に通るので、途中に置くとその下のエラーを拾えません。

err.status が無いときは 500 に落とします。想定していなかった失敗は、こちらの都合の失敗だからです。

完成条件

エラー応答を {"error":"理由"} の形に統一してください。

各ルートは next(err) で投げ、共通のハンドラが受け止めます。ステータスは err.status を使い、無ければ 500 にします。

エラーの中身は外に出しません。 500 のときは決まった文言を返してください。

要件

  1. すべてのルートより後に、引数4つのエラー用ミドルウェアを置く
  2. err.status があればそれを、無ければ 500 を使う
  3. 500 のときは {"error":"サーバーで問題が起きました"} を返し、エラーの中身は外に出さない

入出力例

request("GET", "/items/1") → "200 {"id":1,"name":"レザースニーカー","price":4800}" request("GET", "/items/999") → "404 {"error":"商品が見つかりません"}" request("GET", "/boom") → "500 {"error":"サーバーで問題が起きました"}"

ヒント

前のヒントを開くと次が開きます。開かずに解けると未開封クリアです

ルートの側はもう `next(err)` で投げています。足りないのは受け皿です

ヒント 2ヒント 1 を開くと読めます
ヒント 3ヒント 2 を開くと読めます
生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/10

関連レッスン

  • #108 他人の出品を値下げできてしまう

    出品者だけが値下げできるよう、401と403を使い分けて守ります。

  • #109 同じ商品が2人に売れた

    確認と更新の隙間をトランザクションで塞ぎ、二重販売を止めます。

  • #110 新機能「いいね」を設計から

    付ける・外す・件数取得の3エンドポイントを、設計から任されます。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • スタック後入れ先出し(LIFO)のデータ構造
  • 配列サイズ固定の同型データの集まり
  • 引数位置引数=順番で渡す。
  • ステータスコード200/201/404 など結果を示す3桁の数値
  • ミドルウェアリクエストとレスポンスの間に挟まる処理関数。
  • リクエストWeb 通信の基本単位、ブラウザの問い合わせとサーバーの返答
main.js
学習モード
エディタを読み込んでいます

メモ

#107 エラーの形式がバラバラ

⌘S で保存