1
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?

この記事何?

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

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

#111 feat: app.onError for error handling

  // 変更前: エラー処理が面倒だった
  app.use('*', async (c, next) => {
    try {
      await next()
    } catch (err) {
      // 毎回try-catchを書く必要がある
      c.res = c.text('Custom Error Message', { status: 500 })
    }
  })
 // 変更後: Contextを受け取るようになった
onError(err: Error, c: Context) {
  console.error(`${err.message}`)
  const message = 'Internal Server Error'
  return c.text(message, 500)  // ← c.text() が使える!
}

// ユーザーが簡単にカスタマイズできる
app.onError = (err, c) => {
  // デバッグ用にエラーメッセージをヘッダーに追加
  c.header('X-Error-Message', err.message)

  // 本番ではユーザーに見せたくない詳細を隠す
  return c.json({
    error: 'Something went wrong',
    // err.stack は送らない(セキュリティ)
  }, 500)
}
使い方の例
  const app = new Hono()

  // グローバルエラーハンドラー
  app.onError = (err, c) => {
    if (err instanceof ValidationError) {
      return c.json({ error: err.message }, 400)
    }
    if (err instanceof NotFoundError) {
      return c.json({ error: 'Not found' }, 404)
    }
    // その他のエラー
    return c.json({ error: 'Internal error' }, 500)
  }

  app.get('/users/:id', async (c) => {
    const user = await db.findUser(c.req.param('id'))
    if (!user) {
      throw new NotFoundError('User not found')  // ← throw するだけ!
    }
    return c.json(user)
  })

#112 basic auth middleware supports multiple users

変更前: 1人のユーザーしか設定できない

  app.use('/admin/*', basicAuth({
    username: 'admin',
    password: 'secret'
  }))

変更前のコード


  export const basicAuth = (options: { 
    username: string
    password: string
    realm?: string 
  }) => {
    return async (ctx: Context, next: Function) => {
      const user = auth(ctx.req)
      // 1人のユーザーとだけ比較
      const usernameEqual = await timingSafeEqual(options.username, user.username)
      const passwordEqual = await timingSafeEqual(options.password, user.password)

      if (!usernameEqual || !passwordEqual) {
        // 401 Unauthorized
      }
      return next()
    }
  }

変更後のコード

  export const basicAuth = (
    options: { username: string; password: string; realm?: string },
    ...users: { username: string; password: string }[]  // ← 可変長引数で追加ユーザー
  ) => {
    // 最初のユーザーも配列に追加
    users.unshift({ username: options.username, password: options.password })

    return async (ctx: Context, next: Function) => {
      const requestUser = auth(ctx.req)

      if (requestUser) {
        // 全ユーザーをループでチェック
        for (const user of users) {
          const usernameEqual = await timingSafeEqual(user.username, requestUser.username)
          const passwordEqual = await timingSafeEqual(user.password, requestUser.password)
          if (usernameEqual && passwordEqual) {
            return next()  // どれか1人でもマッチすればOK
          }
        }
      }
      // 401 Unauthorized
    }
  }

使い方

  // 複数ユーザーを許可
  app.use('/admin/*', basicAuth(
    { username: 'admin', password: 'admin123' },      // 1人目
    { username: 'editor', password: 'editor456' },    // 2人目
    { username: 'viewer', password: 'viewer789' }     // 3人目
  ))

#113 Make ParamMap Array<[key, value]>.

お、新しいコントリビューター。Amanoさん
https://github.com/usualoma

ParamMapをオブジェクトから配列に変更

変更前: ParamMapはオブジェクト

  export interface ParamMap {
    [key: string]: number
  }

  // 使い方
  const paramMap: ParamMap = {
    'id': 0,
    'name': 1
  }

  // オブジェクトのキーを取得するのが遅い!
  for (const key of Object.keys(paramMap)) {  // ← Object.keys() が遅い
    console.log(key, paramMap[key])
  }

変更後: ParamMapを配列に

  
  export type ParamMap = Array<[string, number]>

  // 使い方
  const paramMap: ParamMap = [
    ['id', 0],
    ['name', 1]
  ]

Object.keys() の内部動作:

  1. オブジェクトのプロパティを列挙
  2. プロトタイプチェーンをチェック
  3. 新しい配列を作成
  4. キーをコピー
    → オーバーヘッドが大きい

配列のループ:

  1. インデックスでアクセス
    → シンプルで速い

114 refactor: directory structure

ディレクトリ構造のリファクタリング

#115 feat: exports RegExpRouter

RegExpRouterをエクスポート

1
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
1
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?