Skip to content

Hono アダプター

@inferdi/hono は Hono v4 のミドルウェアです。ミドルウェアの呼び出しごとに 1 つのリクエストスコープを作成し、それを Hono のコンテキスト変数を通じて公開し、境界の定まったルートパイプラインが完了した後に破棄します。

インストール

bash
pnpm add @inferdi/inferdi @inferdi/hono hono
ts
import { Hono } from 'hono'
import { Container } from '@inferdi/inferdi'
import { inferdiHono, type InferdiHonoScopeEnv } from '@inferdi/hono'

リクエストスコープ

ts
type RequestContext = {
  requestId: string
  userId?: string
}

class Users {
  constructor(readonly request: RequestContext) {}

  profile(id: string) {
    return { id, userId: this.request.userId }
  }
}

const root = new Container()
  .declareScopeInputs<{ request: RequestContext }>()
  .registerClass('users', Users, ['request'], 'scoped')
const openRequestScope = (requestId: string, userId?: string) =>
  root.createScope({ request: { requestId, userId } })
type RequestScope = ReturnType<typeof openRequestScope>
type AppEnv = InferdiHonoScopeEnv<RequestScope>

const app = new Hono<AppEnv>()

app.use('*', inferdiHono({
  container: root,
  createScope: (_root, c) => openRequestScope(
    crypto.randomUUID(),
    c.req.header('x-user-id')
  )
}))

app.get('/users/:id', async (c) => {
  return c.json(await c.var.di.get('users').profile(c.req.param('id')))
})

c.get('di')c.var.di と同等です。

カスタムキー

ts
type AppEnv = InferdiHonoScopeEnv<RequestScope, 'container'>

const app = new Hono<AppEnv>()
app.use('*', inferdiHono({
  container: root,
  key: 'container',
  createScope: (_root, c) => openRequestScope(
    crypto.randomUUID(),
    c.req.header('x-user-id')
  )
}))

app.get('/users/:id', async (c) => {
  return c.json(await c.var.container.get('users').profile(c.req.param('id')))
})

このアダプターは Hono の ContextVariableMap をグローバルに拡張しないため、ミドルウェアの欠落は TypeScript から引き続き見えます。

オプション

オプションデフォルト説明
container必須ルートコンテナ。このミドルウェアによって破棄されることはありません。
key'di'コンテキスト変数のキー。
createScoperoot.createScope()カスタムのリクエストスコープ作成。
setupScopeなしスコープ作成後に追加の初期化を行います。
disposeScopescope.dispose()カスタムの破棄。
autoDisposetruefalse または false を返す述語は所有権を移譲します。
onDisposeErrorconsole.errorクリーンアップ失敗のシンク。

ストリーミング

Hono のストリーミングヘルパーは、ストリームコールバックが終わる前に Response を返すことがあります。skipInferdiDispose(c) を呼び出し、ストリームのライフサイクルからスコープを破棄してください。

ts
import { stream } from 'hono/streaming'
import { skipInferdiDispose } from '@inferdi/hono'

app.get('/events', (c) => {
  skipInferdiDispose(c)

  const scope = c.var.di
  const events = scope.get('events')

  return stream(c, async (s) => {
    try {
      for await (const event of events.subscribe()) {
        await s.write(`data: ${JSON.stringify(event)}\n\n`)
      }
    } finally {
      await scope.dispose()
    }
  })
})

skipInferdiDispose は成功したレスポンスでのみクリーンアップを抑制します。エラーパスでは、それでも破棄します。