0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HonoのマージされたPRを読んでいく#46 - #50

0
Last updated at Posted at 2025-12-09

この記事何?

エンジニア歴1年半。業務で利用しているHonoが大好きだが、TSもプログラミング知識も弱々すぎて上手く使いこなせない。速いはずが多分生かせてない。
ちゃんと理解するためにソースコードリーディングしたい。

でも目的なくソースコード眺めても意味ないし……ねむい……
そうだ!PRを全部読めば、変遷やなぜ変更されているかストーリー的に分かるのでは。
と思ったのでチャレンジしてみる。PR2000以上あるので、全部できるかは知らん。
https://github.com/honojs/hono

#46 Fixed durable objects example

Durable Objects サンプルのバグ修正。

  // 変更前
 export class Counter {
   value: number = 0
   state: DurableObjectState
   app: Hono  // ← 型だけ宣言

   constructor(state: DurableObjectState) {
     this.state = state
     this.app = new Hono()  // ← constructor内で初期化

     this.app.get('/increment', async (c) => {
       // ...
     })
   }
 }

 // 変更後
 export class Counter {
   value: number = 0
   state: DurableObjectState
   app: Hono = new Hono()  // ← フィールド初期化子で初期化

   constructor(state: DurableObjectState) {
     this.state = state
     // appの初期化は不要

     this.app.get('/increment', async (c) => {
       // ...
     })
   }
 }

理由: フィールド初期化子を使う方がTypeScriptの慣例的

#47 なし

#48 feat: Error handling

エラーハンドリング機能を追加。

// 変更前(エラーハンドリングなし)

app.get('/error', () => {
  throw new Error('Something went wrong')
})

// エラーが発生すると... アプリがクラッシュ

// 変更後(エラーハンドリングあり)
//1. グローバルエラーハンドラー(デフォルト)

// src/hono.ts
async fetch(request: Request, env?: Env, event?: FetchEvent): Promise<Response> {
  return this.dispatch(request, env, event).catch((err) => {
    return this.onError(err)  // ← エラーをキャッチ
  })
}

async handleEvent(event: FetchEvent): Promise<Response> {
  return this.dispatch(event.request, {}, event).catch((err) => {
    return this.onError(err)  // ← エラーをキャッチ
  })
}

// デフォルトのエラーハンドラー
onError(err: any) {
  console.error(err)
  return new Response('Internal Server Error', { status: 500 })
}

処理の流れ:
リクエスト
↓
dispatch() でエラー発生
↓
.catch() でキャッチ
↓
onError() が呼ばれる
↓
500 Internal Server Error を返す

カスタムエラーハンドラー(ミドルウェア)

// カスタムエラーハンドリング
  app.use('*', async (c, next) => {
    try {
      await next()  // 次の処理を実行
    } catch (err) {
      console.error(`${err}`)
      // カスタムエラーレスポンスを返す
      c.res = new Response('Custom Error Message', { status: 500 })
    }
  })

  app.get('/error', () => {
    throw Error('Error has occurred')
  })

処理の流れ:
GET /error
↓
ミドルウェアの try ブロック
↓
await next() → ハンドラー実行
↓
throw Error() でエラー発生
↓
catch ブロックでキャッチ
↓
カスタムエラーレスポンスを返す

#49 feat: Add content-length

Content-Length ヘッダーを自動的に追加。

変更前

app.get('/text', (c) => c.text('abcdefg'))

// レスポンス:
// Status: 200
// Headers: 
//   Content-Type: text/plain
//   (Content-Lengthなし)

変更後

  // src/middleware/default.ts
  export const defaultMiddleware = async (c: Context, next: Function) => {
    // ... 既存の処理

    await next()

    // レスポンスボディがある場合、Content-Lengthを追加
    if (c.res.body) {
      const buff = await c.res.clone().arrayBuffer()
      c.res.headers.append('Content-Length', buff.byteLength.toString())
    }
  }

  app.get('/text', (c) => c.text('abcdefg'))

  // レスポンス:
  // Status: 200
  // Headers:
  //   Content-Type: text/plain
  //   Content-Length: 7  ← 自動追加

Content-Length とは

  • レスポンスボディのバイト数を示すHTTPヘッダー
  • クライアントがボディの長さを事前に知ることができる

#50 なし

HTTPヘッダーとか基本的な知識だろうけど、自分が使用するときにならないと意識できないのでPRを通じて確認できてよかった。今日はここまで。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?