この記事はHono Advent Calendar 2025の2日目の記事です。
はじめに
皆さんはHonoのMiddlewareを書いたことがありますか?
HonoのドキュメントのCustom Middlewareから引用しますが、HonoのMiddlewareは非常にシンプルな仕組みで、以下のように app.use() に渡したコールバック関数内の、 await next() 呼び出しの前後でContextに対して処理を行うことができます。
// Custom logger app.use(async (c, next) => { console.log(`[${c.req.method}] ${c.req.url}`) // ↑次のMiddleware / Handlerの処理の前に実行 await next() }) // Add a custom header app.use('/message/*', async (c, next) => { await next() // ↓前のMiddleware / Handlerの処理の後に実行 c.header('x-message', 'This is middleware!') })
以下はHonoのドキュメントのModify the Response After Nextの例ですが、このように、Handlerが返したResponseをMiddlewareで上書きすることもできてしまいます。
const stripRes = createMiddleware(async (c, next) => { await next() c.res = undefined // Responseを上書き c.res = new Response('New Response') })
このように、HonoのMiddlewareは、リクエスト / レスポンスの処理に介入して好きに加工を行える、大きな柔軟性を持っています。パッと見ただけでも、これなら何でもできそう!という印象になると思います。
ですが、ちょっと待ってください。
何でもできそうといっても、これはTypeScript / JavaScriptで書ける範囲なら何でもできそうという話のようにも見えます。
人によっては、Goの豊富なライブラリを使ってHonoのMiddlewareを書きたいことがあるかもしれないですよね!
そんなあなたのために、hono-middleware-goを紹介します!
hono-middleware-go
hono-middleware-goは、GoでHonoのMiddlewareを書くためのライブラリです。
厳密には、syumai/workersのexp/hono packageを使い、Goで実装されたHono向けMiddlewareを、実際にHonoで動かすためのライブラリとなります。
実は、作ったのは最近のことではなく、1年以上が経過していたようです。今回、この記事を書くにあたって、最新版のGoで動作することを確認しました。
なお、最新のTinyGoでは動作確認がうまくいっていないので、現状はGo専用となります点にご注意ください。
GoによるMiddlewareの実装方法
Middlewareの実装方法はいたってシンプルで、HonoのMiddlewareの形式に則った以下のような関数を実装して、hono.ServeMiddleware関数でServeするだけです。
package import ( "fmt" "github.com/syumai/workers/exp/hono" ) func Middleware(c *hono.Context, next func()) { fmt.Println("middleware started") next() fmt.Println("middleware finished") // ここでContextもいじれる // 例: c.SetStatus(400) } func main() { hono.ServeMiddleware(Middleware) }
あとは、上記のコードをWasmにコンパイルした結果をHonoアプリケーション側で読み込んで、 hono-middleware-go から go 関数をインポートしてMiddlewareとして設定します。以下はCloudflare Workersで使った例です。
import { Hono } from "hono"; import { go } from "@syumai/hono-middleware-go"; import mod from "../wasm/middleware.wasm"; const app = new Hono(); // ここでGoで書いたMiddlewareを設定 app.use(go(mod)); // 各Handlerの設定は普段通り app.get("/", (c) => { return c.text("Hello Hono!"); }); export default app;
実際に、この仕組みを使って実装したBasic認証のサーバーを以下のURLから試せます (ユーザー名: user、パスワード: password)。ぜひ試してみてください!
https://hono-middleware-go-basic-auth.syumai.workers.dev/
GitHub: https://github.com/syumai/hono-middleware-go/tree/main/examples/basic-auth-middleware-go
応用例
もちろんですが、 exp/hono では、Contextからリクエスト・レスポンスを読み取ることができます。
これを活用して、プロキシした画像をグレースケール加工するHono MiddlewareをGoで実装した例が以下です。
https://github.com/syumai/hono-middleware-go/blob/main/examples/gray-scale-middleware-go/go/main.go
詳細な解説は割愛しますが、 c.ResponseBody() で、Handlerから返るレスポンスを io.ReadCloser として取得できるので、その中身を image packageでデコードし、加工して改めて返しています。
こちらのサンプルは下記URLから試せます。
- プロキシしたsyumaiのアバター画像の表示 https://hono-middleware-go-gray-scale.syumai.workers.dev/image
- プロキシしたsyumaiのアバター画像をグレースケール化したものの表示https://hono-middleware-go-gray-scale.syumai.workers.dev/image-gray
おわりに
今回は、Goを使ったHono Middlewareの実装について簡単に紹介しました。 HonoのMiddlewareはインタフェースがシンプルなので、このように他言語を混ぜ込みつつ実装するのも、ちょっとだけ頑張ったら全然可能です。
ぜひ、みなさんも好きな言語でHonoのMiddlewareを書けるようにしてみてください!Goならすぐに試せます!!