この記事何?
エンジニア歴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を通じて確認できてよかった。今日はここまで。