
Hono Middleware
- 315 installs
- 63 repo stars
- Updated July 18, 2026
- bobmatnyc/claude-mpm-skills
hono-middleware is an agent skill that teaches Hono middleware patterns—createMiddleware composition, onion execution order, and 25+ built-in middleware—for developers securing and observing edge or Node HTTP APIs.
About
hono-middleware is a Claude MPM skill in bobmatnyc/claude-mpm-skills covering Hono's onion middleware model for ultrafast web APIs on Cloudflare Workers or Node. The skill documents inline and reusable middleware via createMiddleware from hono/factory, type-safe context variables with c.set, and chaining through app.use with correct pre- and post-handler ordering. It catalogs 25+ built-in middleware such as logger, cors, compress, basicAuth, and rate limiting, plus composition helpers like every and some. Developers reach for it when adding authentication guards, CORS policies, request logging, compression, or custom context propagation to Hono routes. The skill is one of seven Hono skills in the repository alongside core routing, validation, RPC, testing, JSX, and Cloudflare guides, and its SKILL.md spans roughly 584 lines of practical recipes.
- Hono middleware patterns
- Auth and CORS setup
- Request context wiring
- Edge-runtime compatibility
- Composable handler chains
Hono Middleware by the numbers
- 315 all-time installs (skills.sh)
- Ranked #1,296 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 1, 2026 (Skillselion catalog sync)
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill hono-middlewareAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 315 |
|---|---|
| repo stars | ★ 63 |
| Last updated | July 18, 2026 |
| Repository | bobmatnyc/claude-mpm-skills ↗ |
How do you compose auth and CORS middleware in Hono?
Author, compose, and debug Hono middleware for auth, logging, CORS, rate limits, and request context in edge or Node API services.
Who is it for?
Backend developers building Hono APIs on Cloudflare Workers or Node who need predictable middleware ordering and reusable auth, CORS, and logging layers.
Skip if: Teams using Express or Fastify exclusively without Hono, or frontend-only tasks that do not touch HTTP middleware on the server.
When should I use this skill?
The developer asks to add, debug, or reorder Hono middleware for authentication, CORS, logging, compression, rate limits, or shared request context.
What you get
Typed Hono middleware chains, context variables, and configured built-in middleware for auth, logging, CORS, and rate limiting
- Reusable Hono middleware modules
- Configured middleware chains
- Typed request context setup
By the numbers
- Catalogs 25+ built-in Hono middleware modules
- One of 7 Hono skills in bobmatnyc/claude-mpm-skills
- SKILL.md spans roughly 584 lines of middleware guidance
Files
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 12msPretty 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 JSONCompress
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 trackingRelated 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
{
"name": "hono-middleware",
"version": "1.0.0",
"category": "toolchain",
"toolchain": "javascript",
"framework": "hono",
"tags": [
"hono",
"middleware",
"cors",
"authentication",
"jwt",
"logging",
"compression",
"security",
"rate-limiting",
"caching"
],
"entry_point_tokens": 80,
"full_tokens": 4500,
"related_skills": [
"hono-core",
"hono-validation",
"hono-cloudflare"
],
"author": "Claude MPM Team",
"license": "MIT",
"requires": [
"hono-core"
],
"updated": "2025-01-03",
"source_path": "SKILL.md",
"source": "https://hono.dev",
"created": "2025-01-03",
"modified": "2025-01-03",
"maintainer": "Claude MPM Team",
"attribution_required": true,
"repository": "https://github.com/bobmatnyc/claude-mpm-skills",
"progressive_disclosure": {
"enabled": false,
"references": []
},
"skill_level": "intermediate",
"complexity": "intermediate",
"coverage": [
"middleware-basics",
"execution-order",
"context-variables",
"cors",
"authentication",
"jwt",
"logging",
"compression",
"security-headers",
"rate-limiting"
]
}
Related skills
How it compares
Pick hono-middleware when building on Hono's lightweight edge runtime instead of Express-style stack middleware guides.
FAQ
What middleware patterns does hono-middleware teach?
hono-middleware documents Hono's onion execution model where middleware runs before handlers via next() and can modify responses afterward. It covers inline app.use handlers, reusable createMiddleware factories, typed Variables on Context, and chaining multiple middleware with co
How many built-in Hono middleware options are covered?
hono-middleware catalogs more than 25 built-in middleware modules from hono/middleware and related imports, including logger, cors, compress, basicAuth, and rate limiting, with configuration examples for common API cross-cutting concerns.
When should developers use createMiddleware in Hono?
hono-middleware recommends createMiddleware from hono/factory when middleware must be reused across routes and share typed context variables via c.set. That pattern preserves TypeScript inference for downstream handlers better than anonymous inline app.use callbacks.