12k
All articles

Express アプリを Hono に移行する際のヒント

ExpressアプリをHonoへ移行? ルーティング、ミドルウェア、本文解析、エラー処理、段階的移行の違いを解説。

OpenReplay Team
OpenReplay Team
Express アプリを Hono に移行する際のヒント

Express API を保守していて、Hono への移行を検討しているなら、まず理解しておくべきは、これは単純な置換による移行ではないということです。Express は Node の http モジュールと独自の req/res オブジェクトの上に構築されています。一方、Hono は Fetch API と Web 標準の上に構築されています。この違いがすべて—ルーティング、ミドルウェア、リクエスト処理、レスポンス—に影響します。

着手する前に押さえておくべきポイントを以下にまとめます。

重要なポイント

  • Express と Hono は根本的に異なる基盤、すなわち Node の http モジュールと Fetch API および Web 標準の上に成り立っています。
  • Hono は Express の別個の reqres オブジェクトを単一のコンテキストオブジェクト c に置き換え、ハンドラは Response を返す必要があります。
  • ミドルウェアのシグネチャも異なります。Express は (req, res, next) を使うのに対し、Hono は (c, next) を使い、next() を await します。
  • ボディのパースはグローバルミドルウェアではなく、Hono のハンドラ内で直接行います。
  • ステートレスな JSON ルートから始める段階的な移行は、全面的な書き直しよりもはるかに安全です。

まずアーキテクチャの違いを理解する

Express は Node の IncomingMessageServerResponse をラップしています。すべての req および res オブジェクトは Node 固有のものです。対照的に、Hono は標準的な Request および Response オブジェクト—ブラウザや Cloudflare Worker で使うものと同じ—を扱います。

Node.js 上で Hono を動かす場合は、@hono/node-server アダプターを使って橋渡しします。ただし、ハンドラ自体はランタイム非依存のままです。

// Express
app.get('/users/:id', async (req, res) => {
  const user = await db.findById(req.params.id)
  res.json(user)
})

// Hono
app.get('/users/:id', async (c) => {
  const user = await db.findById(c.req.param('id'))
  return c.json(user)
})

形は似ていますが、c(コンテキストオブジェクト)が reqres の両方を置き換えています。res のメソッドを呼び出すのではなく、Response を返す形になります。

Node.js API を Hono に移行する: ステートレスなルートから始める

Express アプリを Hono に移植する際、最も安全な出発点はステートレスで JSON のみを扱うルートです—ファイルシステムアクセスや Node 固有のストリーム、セッションミドルウェアを伴わないものです。これらはきれいに変換できます。

Node 固有の API(req.socketres.localsres.sendFile)に依存するルートはより慎重な検討が必要です。本格的に移行に踏み切る前に、まずそれらを切り分け、扱う対象を明確にしておきましょう。

Hono ミドルウェアの移行: 互換性を当然視しない

Express のミドルウェアは (req, res, next) パターンに従います。Hono のミドルウェアは (c, next) を使い、通常は await next() を呼び出します。両者は互換性がありません

// Express ミドルウェア
app.use((req, res, next) => {
  req.startTime = Date.now()
  next()
})

// Hono での同等のもの
app.use(async (c, next) => {
  c.set('startTime', Date.now())
  await next()
})

一般的なパッケージについては、Hono に公式の同等品があります:

ExpressHono の同等品
cors()hono/cors
helmet()hono/secure-headers
express.json()c.req.json() 経由でビルトイン
morganhono/logger またはカスタムミドルウェア

Express のミドルウェアをラップしようとするのではなく、意図的に書き直しましょう—抽象化がきれいに保たれることは稀です。

リクエストボディとレスポンスの扱い

Express 5 では、ボディのパースは依然としてミドルウェアベースです。Hono では、ボディをハンドラ内で直接パースします:

// Hono のボディパース
app.post('/items', async (c) => {
  const body = await c.req.json()
  return c.json({ received: body }, 201)
})

レスポンスは常に return するもので、変更(mutate)するものではありません。res.status(201).json(...) のような書き方はなく、代わりにステータスを c.json() の第 2 引数として渡します。

エラーハンドリング

Express は 4 引数のエラーハンドラ (err, req, res, next) を使います。Hono は app.onError を使います:

app.onError((err, c) => {
  console.error(err)
  return c.json({ error: 'Internal Server Error' }, 500)
})

一度にではなく、段階的に移行する

全面的な書き直しがスムーズに進むことは稀です。より良いアプローチは:

  1. 一度に 1 つの独立したルートグループを移植する。
  2. 移行期間中は Hono を Express と並行して動かす。
  3. ミドルウェアは明示的に書き直す—ラップしない。
  4. Node 固有の依存関係(ファイル処理、レガシー認証)は最後に回す。

まとめ

Express アプリの Hono への移植は、2 つのフレームワークが異なる基盤の上に成り立っていることを受け入れれば、難しくはありません。ルーティング構文は十分に馴染みがあり、ほとんどのルートは数分で変換できます。本当に労力を要するのは、ミドルウェアと、Node 固有の API を直接触っている部分です。それらに対して計画的に取り組めば、移行は十分に管理可能なものになります。

FAQ

移行中に Hono と Express を並行して動かせますか?

はい。よくあるパターンは、両者の前段にリバースプロキシや小さな Node のエントリポイントを置き、特定のパスを Hono アプリにルーティングし、残りは Express に任せる方法です。これにより、ルートグループを 1 つずつ移行でき、問題が起きた場合にも素早くロールバックできます。リスクの高い一括切り替えに踏み切る必要はありません。

Hono は Node.js でも動きますか、それともエッジランタイム専用ですか?

Hono は Node.js、Bun、Deno、Cloudflare Workers、AWS Lambda、その他いくつかのランタイムで動作します。Node 上では、@hono/node-server アダプターを使って、Fetch API モデルと Node の http モジュールを橋渡しします。同じハンドラのコードがランタイム間で移植可能であり、これが Express に対する Hono の大きな利点の 1 つです。

既存の Express ミドルウェアを Hono で再利用する方法はありますか?

基本的にはありません。Express のミドルウェアは Node の req と res オブジェクト、そして next コールバックに依存していますが、Hono のミドルウェアは Fetch スタイルのコンテキスト上で動作します。コミュニティ製のアダプターもいくつか存在しますが、壊れやすい傾向があります。推奨されるのは Hono の API に合わせてミドルウェアを書き直すことです。よくあるニーズの多くは、すでに公式またはビルトインの同等品でカバーされています。

Express と Hono のパフォーマンスはどう違いますか?

Hono は一般に Express より高速で、特にルーティングと JSON レスポンスでその傾向が顕著です。これは軽量なルーターと Fetch ベースの設計のおかげです。実際にどの程度の向上が得られるかはワークロード次第で、通常はデータベースクエリや外部呼び出しが支配的になります。パフォーマンスは移行による有用な副次効果と捉えるべきで、ベンチマークでルーティングのオーバーヘッドが本当のボトルネックだと示されない限り、主な動機にすべきではありません。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.