Agent skill
hono-middleware
Hono middleware patterns - creation, composition, built-in middleware, and execution order for web applications
Install this agent skill to your Project
npx add-skill https://github.com/bobmatnyc/claude-mpm-skills/tree/main/toolchains/javascript/frameworks/hono/middleware
SKILL.md
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()
// Simple logging middleware
app.use('*', async (c, next) => {
console.log(`[${c.req.method}] ${c.req.url}`)
await next()
})
// Path-specific middleware
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!')
})
// Output:
// 1. Before (first in)
// 2. Before (second in)
// 3. Before (third in)
// Handler
// 4. After (third out)
// 5. After (second out)
// 6. After (first out)
Creating Reusable Middleware
import { createMiddleware } from 'hono/factory'
// Type-safe reusable middleware
const logger = createMiddleware(async (c, next) => {
console.log(`[${new Date().toISOString()}] ${c.req.method} ${c.req.path}`)
await next()
})
// Middleware with options
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'
// Define variable types
type Variables = {
user: { id: string; email: string; role: string }
requestId: string
}
const app = new Hono<{ Variables: Variables }>()
// Auth middleware sets user
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) // Type-safe!
await next()
})
// Request ID middleware
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') // Type: { id, email, role }
const reqId = c.get('requestId') // Type: string
return c.json({ user, requestId: reqId })
})
Built-in Middleware
CORS
import { cors } from 'hono/cors'
// Simple - allow all origins
app.use('/api/*', cors())
// Configured
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
}))
// Dynamic origin
app.use('/api/*', cors({
origin: (origin) => {
return origin.endsWith('.example.com')
? origin
: 'https://example.com'
}
}))
Bearer Auth
import { bearerAuth } from 'hono/bearer-auth'
// Simple token validation
app.use('/api/*', bearerAuth({ token: 'my-secret-token' }))
// Multiple tokens
app.use('/api/*', bearerAuth({
token: ['token1', 'token2', 'token3']
}))
// Custom verification
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' // pragma: allowlist secret
}))
// Multiple users
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' // pragma: allowlist secret
}))
// Access payload in handler
app.get('/api/profile', (c) => {
const payload = c.get('jwtPayload')
return c.json({ userId: payload.sub })
})
// With algorithm
app.use('/api/*', jwt({
secret: 'secret', // pragma: allowlist secret
alg: 'HS256'
}))
Logger
import { logger } from 'hono/logger'
// Default format
app.use(logger())
// Custom format
app.use(logger((str, ...rest) => {
console.log(`[API] ${str}`, ...rest)
}))
// Output: <-- GET /api/users
// --> GET /api/users 200 12ms
Pretty JSON
import { prettyJSON } from 'hono/pretty-json'
// Add ?pretty to format JSON responses
app.use(prettyJSON())
// GET /api/users → {"users":[...]}
// GET /api/users?pretty → formatted JSON
Compress
import { compress } from 'hono/compress'
app.use(compress())
// With options
app.use(compress({
encoding: 'gzip' // 'gzip' | 'deflate'
}))
ETag
import { etag } from 'hono/etag'
app.use(etag())
// Weak ETags
app.use(etag({ weak: true }))
Cache
import { cache } from 'hono/cache'
// Cloudflare Workers cache
app.use('/static/*', cache({
cacheName: 'my-app',
cacheControl: 'max-age=3600'
}))
Secure Headers
import { secureHeaders } from 'hono/secure-headers'
app.use(secureHeaders())
// Configured
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())
// With options
app.use(csrf({
origin: ['https://example.com']
}))
Timeout
import { timeout } from 'hono/timeout'
// 5 second timeout
app.use('/api/*', timeout(5000))
// Custom error
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
// Apply middleware based on condition
const conditionalAuth = createMiddleware(async (c, next) => {
// Skip auth for health checks
if (c.req.path === '/health') {
return next()
}
// Apply auth for everything else
const token = c.req.header('Authorization')
if (!token) {
return c.json({ error: 'Unauthorized' }, 401)
}
await next()
})
Middleware Composition
import { every, some } from 'hono/combine'
// All middleware must pass
const strictAuth = every(
bearerAuth({ token: 'secret' }),
ipRestriction(['192.168.1.0/24']),
rateLimiter({ max: 100 })
)
// Any middleware can pass
const flexibleAuth = some(
bearerAuth({ token: 'api-key' }),
basicAuth({ username: 'user', password: 'pass' }) // pragma: allowlist secret
)
app.use('/api/*', strictAuth)
app.use('/public/*', flexibleAuth)
Modifying Responses
const addHeaders = createMiddleware(async (c, next) => {
await next()
// Modify response after handler
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()
// Replace response entirely
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 // Re-throw HTTP exceptions
}
// Log and convert other errors
console.error('Middleware error:', error)
throw new HTTPException(500, { message: 'Internal error' })
}
})
Rate Limiting
// Simple in-memory rate limiter
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()
// 1. Request ID (first - for tracking)
app.use(requestId())
// 2. Logger (early - to log all requests)
app.use(logger())
// 3. Security headers
app.use(secureHeaders())
// 4. CORS (before auth - for preflight)
app.use('/api/*', cors())
// 5. Compression
app.use(compress())
// 6. Rate limiting
app.use('/api/*', rateLimiter({ max: 100, window: 60000 }))
// 7. Authentication
app.use('/api/*', bearerAuth({ verifyToken }))
// 8. Request validation (after auth)
app.use('/api/*', validator)
// 9. Routes
app.route('/api', apiRoutes)
// 10. Not found handler (last)
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 # Zod validation
npm install @hono/graphql-server # GraphQL
npm install @hono/swagger-ui # Swagger UI
npm install @hono/prometheus # Prometheus metrics
npm install @hono/sentry # Sentry error tracking
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
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
bad-example-skill
ANTI-PATTERN - Example showing violations of self-containment (DO NOT COPY)
example-framework-skill
Example of a properly self-contained skill following all best practices
systematic-debugging
Systematic debugging methodology emphasizing root cause analysis over quick fixes
verification-before-completion
Run verification commands and confirm output before claiming success
Root Cause Tracing
Systematically trace bugs backward through call stack to find original trigger
Brainstorming Ideas Into Designs
Interactive idea refinement using Socratic method to develop fully-formed designs
Didn't find tool you were looking for?