メインコンテンツへスキップ
見出し画像

Hono のエラー設計。 ログと例外を 「最初に」 決める理由

    「とりあえず console.log」 で地獄を見たこと、ありませんか?

    API サーバーを作るとき、一番楽しいのは「正常系」の実装です。
    データを取得して、加工して、JSON で返す。
    Postman で叩いて `200 OK` が返ってきたときの快感。
    わかります。

    でも、リリース後に僕たちを待っているのは、容赦ない「異常系」の現実です。

    「データが見つからない」
    「DB 接続がタイムアウトした」
    「予期せぬ入力が来た」

    そんなとき、ログを見に行ったら `undefined` とだけ表示されていたり、あるいは何も出力されていなかったりして、絶望した経験はないでしょうか?

    「エラーハンドリングは、最後にまとめてやればいいや」

    かつての僕はそう思っていました。
    しかし、断言します。
    それは死亡フラグです。

    ビジネスロジックを書き始める前に、まず「どう転ぶか(例外)」と「どう記録するか(ログ)」を決める。
    いわば「受け身の取り方」を最初に設計しておくことこそが、個人開発を長く続けるための生存戦略なのです。

    今日は、軽量フレームワーク「Hono」を題材に、僕が実践している「最初に決めるべきエラー設計」についてお話しします。

    なぜ 「最初に」 決める必要があるのか

    理由はシンプルです。
    後からエラーハンドリングを追加すると、コードがスパゲッティになるからです。

    設計なしに進めると、あちこちのコントローラーで:

    try {
      // 処理
    } catch (e) {
      console.error(e)
      return c.json({ message: 'エラーです' }, 500)
    }

    こんなコードを量産することになります。
    これでは、以下の問題が起きます。

    📌 ログが統一されない:
    `console.log`、`console.error`、あるいはライブラリのロガーが混在し、CloudWatch や Sentry で検索できない。

    📌 ステータスコードが適当:
    本来 `400`(クライアントのミス)であるべきエラーが、全部 `500`(サーバーエラー)で返される。

    📌 詳細が消える:
    `e.message` だけ返してしまい、スタックトレースや発生時のコンテキスト(ユーザー ID など)が闇に葬られる。

    だからこそ、最初に「型」を作るのです。

    Hono におけるエラー設計の勘所

    Hono は非常にシンプルで柔軟なフレームワークですが、エラーハンドリングにおいても強力な機能を提供してくれています。
    押さえるべきポイントは 2 つです。

    `onError` ミドルウェアを 「最後の砦」 にする

    各ルートハンドラ(`app.get` など)で `try-catch` を書くのをやめましょう。
    Hono には、キャッチされなかった例外をフックできる `onError` という仕組みがあります。

    ここで一元的にエラーを処理することで、個々のロジックからノイズ(エラー処理コード)を消し去ることができます。

    「例外」 を共通言語にする

    「ユーザーが見つからない」も「DB エラー」も、プログラム上はただの `Error` オブジェクトです。
    しかし、これらを区別しなければなりません。

    そこで、独自のカスタム例外クラスを作ります。
    「この例外が投げられたら 404 を返す」「この例外なら 500」というルールをクラスに持たせるのです。

    こうすることで、例外は「バグ」ではなく、「システムからの正規のメッセージ」へと昇華されます。

    では、具体的にどう実装すればいいのか?
    ここからは、僕が実際のプロダクトで使っている「カスタム例外クラス」と「鉄壁の `onError` 実装」のコードを、コピペで使える形で公開します。

     
     
    15年のフリーランス / ソフトウェアエンジニア経験を基に、仕事が劇的にラクになる思考法を発信し、あなたの生産性と心の余裕を生み出します。吃音で選んだ物語の力で、人生の深い葛藤を乗り越えるヒントと癒しを提供。フォローで専門知識と心の拠り所となる物語が手に入ります。

    あなたへのおすすめ