コンテンツへスキップ

ミドルウェア

ミドルウェアはハンドラーの前後で作動します。ディスパッチ前に`Request`を取得したり、ディスパッチ後に`Response`を操作できます。

ミドルウェアの定義

  • ハンドラー - `Response`オブジェクトを返す必要があります。呼び出されるハンドラーは1つだけです。
  • ミドルウェア - 何も返す必要はなく、`await next()`を使って次のミドルウェアに処理が渡されます。

ユーザーは`app.use`を使ってミドルウェアを登録できます。また、ハンドラーと同様に`app.HTTP_METHOD`も使用できます。この機能により、パスとメソッドを簡単に指定できます。

ts
// match any method, all routes
app.use(logger())

// specify path
app.use('/posts/*', cors())

// specify method and path
app.post('/posts/*', basicAuth())

ハンドラーが`Response`を返した場合、エンドユーザーに使用され、処理が停止します。

ts
app.post('/posts', (c) => c.text('Created!', 201))

この場合、以下のように4つのミドルウェアがディスパッチ前に処理されます。

ts
logger() -> cors() -> basicAuth() -> *handler*

実行順序

ミドルウェアの実行順序は、登録された順序によって決定されます。最初に登録されたミドルウェアの`next`前の処理が最初に実行され、`next`後の処理が最後に実行されます。以下を参照してください。

ts
app.use(async (_, next) => {
  console.log('middleware 1 start')
  await next()
  console.log('middleware 1 end')
})
app.use(async (_, next) => {
  console.log('middleware 2 start')
  await next()
  console.log('middleware 2 end')
})
app.use(async (_, next) => {
  console.log('middleware 3 start')
  await next()
  console.log('middleware 3 end')
})

app.get('/', (c) => {
  console.log('handler')
  return c.text('Hello!')
})

結果は次のようになります。

middleware 1 start
  middleware 2 start
    middleware 3 start
      handler
    middleware 3 end
  middleware 2 end
middleware 1 end

組み込みミドルウェア

Honoには組み込みミドルウェアがあります。

ts
import { Hono } from 'hono'
import { poweredBy } from 'hono/powered-by'
import { logger } from 'hono/logger'
import { basicAuth } from 'hono/basic-auth'

const app = new Hono()

app.use(poweredBy())
app.use(logger())

app.use(
  '/auth/*',
  basicAuth({
    username: 'hono',
    password: 'acoolproject',
  })
)

警告

Denoでは、Honoのバージョンとは異なるバージョンのミドルウェアを使用できますが、バグにつながる可能性があります。たとえば、バージョンが異なるため、このコードは機能しません。

ts
import { Hono } from 'jsr:@hono/hono@4.4.0'
import { upgradeWebSocket } from 'jsr:@hono/hono@4.4.5/deno'

const app = new Hono()

app.get(
  '/ws',
  upgradeWebSocket(() => ({
    // ...
  }))
)

カスタムミドルウェア

`app.use()`内で直接独自のミドルウェアを作成できます。

ts
// Custom logger
app.use(async (c, next) => {
  console.log(`[${c.req.method}] ${c.req.url}`)
  await next()
})

// Add a custom header
app.use('/message/*', async (c, next) => {
  await next()
  c.header('x-message', 'This is middleware!')
})

app.get('/message/hello', (c) => c.text('Hello Middleware!'))

ただし、`app.use()`内にミドルウェアを直接埋め込むと、再利用性が制限される可能性があります。そのため、ミドルウェアを別々のファイルに分離できます。

ミドルウェアを分離する際に`context`と`next`の型定義を失わないように、Honoのファクトリーから`createMiddleware()`を使用できます。

ts
import { createMiddleware } from 'hono/factory'

const logger = createMiddleware(async (c, next) => {
  console.log(`[${c.req.method}] ${c.req.url}`)
  await next()
})

情報

`createMiddleware`では型ジェネリクスを使用できます。

ts
createMiddleware<{Bindings: Bindings}>(async (c, next) =>

Nextの後のResponseの変更

さらに、必要に応じてレスポンスを変更するようにミドルウェアを設計できます。

ts
const stripRes = createMiddleware(async (c, next) => {
  await next()
  c.res = undefined
  c.res = new Response('New Response')
})

ミドルウェア引数内でのコンテキストアクセス

ミドルウェア引数内でコンテキストにアクセスするには、`app.use`によって提供されるコンテキストパラメーターを直接使用します。詳しくは、以下の例を参照してください。

ts
import { cors } from 'hono/cors'

app.use('*', async (c, next) => {
  const middleware = cors({
    origin: c.env.CORS_ORIGIN,
  })
  return middleware(c, next)
})

ミドルウェアでのコンテキストの拡張

ミドルウェア内でコンテキストを拡張するには、`c.set`を使用します。`createMiddleware`関数に`{ Variables: { yourVariable: YourVariableType } }`ジェネリック引数を渡すことで、型安全にすることができます。

ts
import { createMiddleware } from 'hono/factory'

const echoMiddleware = createMiddleware<{
  Variables: {
    echo: (str: string) => string
  }
}>(async (c, next) => {
  c.set('echo', (str) => str)
  await next()
})

app.get('/echo', echoMiddleware, (c) => {
  return c.text(c.var.echo('Hello!'))
})

サードパーティミドルウェア

組み込みミドルウェアは外部モジュールに依存しませんが、サードパーティミドルウェアはサードパーティライブラリに依存する可能性があります。そのため、それらを使用することで、より複雑なアプリケーションを作成できます。

たとえば、GraphQLサーバーミドルウェア、Sentryミドルウェア、Firebase認証ミドルウェアなどがあります。

MITライセンスの下でリリースされています。