| name | hono-middleware |
| description | Hono middleware patterns - creation, composition, built-in middleware, and execution order for web applications |
| user-invocable | false |
| disable-model-invocation | true |
| skill_version | 1.0.0 |
| updated_at | "2025-01-03T00:00:00.000Z" |
| tags | ["hono","middleware","cors","authentication","logging","compression","security"] |
| progressive_disclosure | {"entry_point":{"summary":"Middleware creation, composition, and 25+ built-in middleware for Hono applications","when_to_use":"Adding authentication, CORS, logging, compression, rate limiting, or custom request processing","quick_start":"1. Import middleware from hono/middleware 2. Apply with app.use() 3. Chain multiple middleware"},"references":[]} |
| context_limit | 800 |
Hono Middleware Patterns
Overview
Hono provides a powerful middleware system with an "onion" execution model. Middleware processes requests before handlers and responses after handlers, enabling cross-cutting concerns like authentication, logging, and CORS.
Key Features:
- Onion-style execution order
- Type-safe middleware creation with
createMiddleware
- 25+ built-in middleware
- Context variable passing between middleware
- Async/await support throughout
When to Use This Skill
Use Hono middleware when:
- Adding authentication/authorization
- Implementing CORS for cross-origin requests
- Adding request logging or timing
- Compressing responses
- Rate limiting API endpoints
- Validating requests before handlers
Middleware Basics
Inline Middleware
import { Hono } from 'hono'
const app = new Hono()
app.use('*', async (c, next) => {
console.log(`[${c.req.method}] ${c.req.url}`)
await next()
})
app.use('/api/*', async (c, next) => {
const start = Date.now()
await next()
const ms = Date.now() - start
c.header('X-Response-Time', `${ms}ms`)
})
Execution Order (Onion Model)
app.use(async (c, next) => {
console.log('1. Before (first in)')
await next()
console.log('6. After (first out)')
})
app.use(async (c, next) => {
console.log('2. Before (second in)')
await next()
console.log('5. After (second out)')
})
app.use(async (c, next) => {
console.log('3. Before (third in)')
await next()
console.log('4. After (third out)')
})
app.get('/', (c) => {
console.log('Handler')
return c.text('Hello!')
})
Creating Reusable Middleware
import { createMiddleware } from 'hono/factory'
const logger = createMiddleware(async (c, next) => {
console.log(`[${new Date().toISOString()}] ${c.req.method} ${c.req.path}`)
await next()
})
const timing = (headerName = 'X-Response-Time') => {
return createMiddleware(async (c, next) => {
const start = Date.now()
await next()
c.header(headerName, `${Date.now() - start}ms`)
})
}
app.use(logger)
app.use(timing('X-Duration'))
Context Variables
Passing Data Between Middleware
import { createMiddleware } from 'hono/factory'
type Variables = {
user: { id: string; email: string; role: string }
requestId: string
}
const app = new Hono<{ Variables: Variables }>()
const auth = createMiddleware<{ Variables: Variables }>(async (c, next) => {
const token = c.req.header('Authorization')?.replace('Bearer ', '')
if (!token) {
return c.json({ error: 'Unauthorized' }, 401)
}
const user = await verifyToken(token)
c.set('user', user)
await next()
})
const requestId = createMiddleware<{ Variables: Variables }>(async (c, next) => {
c.set('requestId', crypto.randomUUID())
await next()
})
app.use(requestId)
app.use('/api/*', auth)
app.get('/api/profile', (c) => {
const user = c.get('user')
const reqId = c.get('requestId')
return c.json({ user, requestId: reqId })
})
Built-in Middleware
CORS
import { cors } from 'hono/cors'
app.use('/api/*', cors())
app.use('/api/*', cors({
origin: ['https://example.com', 'https://app.example.com'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
exposeHeaders: ['X-Total-Count'],
credentials: true,
maxAge: 86400
}))
app.use('/api/*', cors({
origin: (origin) => {
return origin.endsWith('.example.com')
? origin
: 'https://example.com'
}
}))
Bearer Auth
import { bearerAuth } from 'hono/bearer-auth'
app.use('/api/*', bearerAuth({ token: 'my-secret-token' }))
app.use('/api/*', bearerAuth({
token: ['token1', 'token2', 'token3']
}))
app.use('/api/*', bearerAuth({
verifyToken: async (token, c) => {
const user = await validateJWT(token)
if (user) {
c.set('user', user)
return true
}
return false
}
}))
Basic Auth
import { basicAuth } from 'hono/basic-auth'
app.use('/admin/*', basicAuth({
username: 'admin',
password: 'secret'
}))
app.use('/admin/*', basicAuth({
verifyUser: (username, password, c) => {
return username === 'admin' && password === process.env.ADMIN_PASSWORD
}
}))
JWT Auth
import { jwt } from 'hono/jwt'
app.use('/api/*', jwt({
secret: 'my-jwt-secret'
}))
app.get('/api/profile', (c) => {
const payload = c.get('jwtPayload')
return c.json({ userId: payload.sub })
})
app.use('/api/*', jwt({
secret: 'secret',
alg: 'HS256'
}))
Logger
import { logger } from 'hono/logger'
app.use(logger())
app.use(logger((str, ...rest) => {
console.log(`[API] ${str}`, ...rest)
}))
Pretty JSON
import { prettyJSON } from 'hono/pretty-json'
app.use(prettyJSON())
Compress
import { compress } from 'hono/compress'
app.use(compress())
app.use(compress({
encoding: 'gzip'
}))
ETag
import { etag } from 'hono/etag'
app.use(etag())
app.use(etag({ weak: true }))
Cache
import { cache } from 'hono/cache'
app.use('/static/*', cache({
cacheName: 'my-app',
cacheControl: 'max-age=3600'
}))
Secure Headers
import { secureHeaders } from 'hono/secure-headers'
app.use(secureHeaders())
app.use(secureHeaders({
contentSecurityPolicy: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"]
},
xFrameOptions: 'DENY',
xXssProtection: '1; mode=block'
}))
CSRF Protection
import { csrf } from 'hono/csrf'
app.use(csrf())
app.use(csrf({
origin: ['https://example.com']
}))
Timeout
import { timeout } from 'hono/timeout'
app.use('/api/*', timeout(5000))
app.use('/api/*', timeout(5000, () => {
return new Response('Request timeout', { status: 408 })
}))
Request ID
import { requestId } from 'hono/request-id'
app.use(requestId())
app.get('/', (c) => {
const id = c.get('requestId')
return c.json({ requestId: id })
})
Advanced Patterns
Conditional Middleware
const conditionalAuth = createMiddleware(async (c, next) => {
if (c.req.path === '/health') {
return next()
}
const token = c.req.header('Authorization')
if (!token) {
return c.json({ error: 'Unauthorized' }, 401)
}
await next()
})
Middleware Composition
import { every, some } from 'hono/combine'
const strictAuth = every(
bearerAuth({ token: 'secret' }),
ipRestriction(['192.168.1.0/24']),
rateLimiter({ max: 100 })
)
const flexibleAuth = some(
bearerAuth({ token: 'api-key' }),
basicAuth({ username: 'user', password: 'pass' })
)
app.use('/api/*', strictAuth)
app.use('/public/*', flexibleAuth)
Modifying Responses
const addHeaders = createMiddleware(async (c, next) => {
await next()
c.res.headers.set('X-Powered-By', 'Hono')
c.res.headers.set('X-Request-Id', c.get('requestId'))
})
const transformResponse = createMiddleware(async (c, next) => {
await next()
const originalBody = await c.res.json()
c.res = new Response(
JSON.stringify({ data: originalBody, timestamp: Date.now() }),
c.res
)
})
Error Handling in Middleware
import { HTTPException } from 'hono/http-exception'
const safeMiddleware = createMiddleware(async (c, next) => {
try {
await next()
} catch (error) {
if (error instanceof HTTPException) {
throw error
}
console.error('Middleware error:', error)
throw new HTTPException(500, { message: 'Internal error' })
}
})
Rate Limiting
const rateLimiter = (options: { max: number; window: number }) => {
const requests = new Map<string, { count: number; reset: number }>()
return createMiddleware(async (c, next) => {
const ip = c.req.header('CF-Connecting-IP') || 'unknown'
const now = Date.now()
let record = requests.get(ip)
if (!record || now > record.reset) {
record = { count: 0, reset: now + options.window }
requests.set(ip, record)
}
record.count++
if (record.count > options.max) {
c.header('Retry-After', String(Math.ceil((record.reset - now) / 1000)))
return c.json({ error: 'Rate limit exceeded' }, 429)
}
c.header('X-RateLimit-Limit', String(options.max))
c.header('X-RateLimit-Remaining', String(options.max - record.count))
await next()
})
}
app.use('/api/*', rateLimiter({ max: 100, window: 60000 }))
Middleware Order Best Practices
const app = new Hono()
app.use(requestId())
app.use(logger())
app.use(secureHeaders())
app.use('/api/*', cors())
app.use(compress())
app.use('/api/*', rateLimiter({ max: 100, window: 60000 }))
app.use('/api/*', bearerAuth({ verifyToken }))
app.use('/api/*', validator)
app.route('/api', apiRoutes)
app.notFound((c) => c.json({ error: 'Not found' }, 404))
Quick Reference
Built-in Middleware
| Middleware | Import | Purpose |
|---|
cors | hono/cors | Cross-origin requests |
bearerAuth | hono/bearer-auth | Bearer token auth |
basicAuth | hono/basic-auth | HTTP Basic auth |
jwt | hono/jwt | JWT verification |
logger | hono/logger | Request logging |
prettyJSON | hono/pretty-json | JSON formatting |
compress | hono/compress | Response compression |
etag | hono/etag | ETag headers |
cache | hono/cache | Response caching |
secureHeaders | hono/secure-headers | Security headers |
csrf | hono/csrf | CSRF protection |
timeout | hono/timeout | Request timeout |
requestId | hono/request-id | Request ID header |
Third-Party Middleware
npm install @hono/zod-validator
npm install @hono/graphql-server
npm install @hono/swagger-ui
npm install @hono/prometheus
npm install @hono/sentry
Related Skills
- hono-core - Framework fundamentals
- hono-validation - Request validation with Zod
- hono-cloudflare - Cloudflare-specific middleware
Version: Hono 4.x
Last Updated: January 2025
License: MIT