🐺
HonoのVariablesでDependency Injectionを実装する
はじめに
Webフレームワークである 🔥Hono を使ったAPI開発において、Dependency Injection(DI)をVariablesを使って実装する方法について素振りした記事です。
HonoのVariablesとは
Honoのコンストラクタには型安全性を確保するため、変数をジェネリックとして渡すことができます。
そしてミドルウェアで依存の注入を行うことができます。
type Variables = {
message: string
}
const app = new Hono<{ Variables: Variables }>()
app.use(async (c, next) => {
c.set('message', 'Hono is cool!!')
await next()
})
app.get('/', (c) => {
const message = c.get('message')
return c.text(`The message is "${message}"`)
})
※同じリクエスト内でのみ保持されます。異なるリクエスト間で共有したり永続化したりすることはできません。
DIパターンの実装
基本的なサービス層の定義
ここではDIコンテナで管理したいサービスを定義します。
// services/UserService.ts
export interface IUserService {
getUser(id: string): Promise<{ id: string; name: string }>
}
export class UserService implements IUserService {
async getUser(id: string) {
// データベースアクセスのロジックがあるとして
return { id, name: `User${id}` }
}
}
DIコンテナの作成
サービスを管理するコンテナを作成します。
// di/container.ts
import { IUserService, UserService } from '../services/UserService'
export interface Container {
userService: IUserService
}
export function createContainer(): Container {
return {
userService: new UserService(),
}
}
Honoアプリケーションでの活用
HonoのVariablesにDIコンテナを注入します。
// app.ts
import { Hono } from 'hono'
import { Container, createContainer } from './di/container'
type Variables = {
container: Container
}
const app = new Hono<{ Variables: Variables }>()
// DIコンテナを注入するミドルウェア
app.use('*', async (c, next) => {
c.set('container', createContainer())
await next()
})
// ユーザー取得API
app.get('/users/:id', async (c) => {
const id = c.req.param('id')
const { userService } = c.get('container')
const user = await userService.getUser(id)
return c.json(user)
})
export default app
シングルトンパターンの実装
Contextオブジェクトはリクエストごとにインスタンス化され、レスポンスが返されるまで保持されるという特徴があります。
パフォーマンス最適化が必要な場合は、シングルトンパターンも検討するのも良さそうです。
// di/container.ts
let containerInstance: Container | null = null
export function getContainer(): Container {
if (!containerInstance) {
containerInstance = createContainer()
}
return containerInstance
}
// app.ts
app.use('*', async (c, next) => {
c.set('container', getContainer())
await next()
})
終わりに
HonoでDIパターン検討していたのでちょうど良さそうなVariablesを使った方法をまとめました。
他にもHonoのDIライブラリもあるようなので、要件に応じて使い分けるのが良さそうです。
Discussion