new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Hono · all subjects

middleware

244 notes in this subject, read out of this brain and free to use. This is page 1 of 5.

Middleware definition and behavior

Middleware should await next() and return nothing to call the next middleware, or return a Response to early-exit. If a handler or middleware returns a Response, it will be used for the end-user and will stop processing. If the handler or any middleware throws, Hono will catch it and either pass it to app.onError() callback or automatically convert it to a 500 response before returning it up the chain of middleware.

Extending Context with Variables in middleware

To extend the context inside middleware, use c.set(). This can be made type-safe by passing a { Variables: { yourVariable: YourVariableType } } generic argument to createMiddleware. Variables are accessed downstream via c.var.yourVariable.

Custom middleware inline in app.use

Custom middleware can be written directly inside app.use(): app.use(async (c, next) => { console.log(`[${c.req.method}] ${c.req.url}`); await next(); }). However, embedding middleware directly limits reusability, so separating middleware into different files using createMiddleware() is recommended.

Built-in middleware import locations

Built-in middleware are imported from specific Hono submodules: poweredBy from 'hono/powered-by', logger from 'hono/logger', basicAuth from 'hono/basic-auth'.

Registering middleware with app.use and HTTP methods

Middleware can be registered using app.use() or using app.HTTP_METHOD() just like handlers. app.use(logger()) matches any method on all routes. app.use('/posts/*', cors()) specifies a path. app.post('/posts/*', basicAuth()) specifies both method and path.

Type inference across chained middleware

When chaining multiple middleware using .use(), Hono automatically accumulates the Variables types. Route handlers that follow the middleware chain can access all variables from every preceding middleware in a type-safe way. Each .use() call returns a new Hono instance with the merged type.

Middleware execution order

Middleware is executed in the order it is registered. The process before the next() of the first registered middleware is executed first, and the process after the next() is executed last. This creates a nested execution pattern where middleware wraps the handler and subsequent middleware.

createMiddleware helper for type-safe middleware

The createMiddleware() helper from 'hono/factory' can be used to create reusable, type-safe middleware. It preserves type definitions for context and next, and allows type-safe access to data set in Context. Type generics can be used with createMiddleware, such as createMiddleware<{Bindings: Bindings}>(async (c, next) => ...).

Accessing context inside middleware arguments

To access context inside middleware arguments, directly use the context parameter provided by app.use. For example, when using cors(), you can access c.env.CORS_ORIGIN by passing middleware(c, next) inside app.use middleware: app.use('*', async (c, next) => { const middleware = cors({ origin: c.env.CORS_ORIGIN }); return middleware(c, next); })

Modifying Response after next in middleware

Middleware can modify responses by awaiting next() first, then modifying c.res. Example: const stripRes = createMiddleware(async (c, next) => { await next(); c.res = undefined; c.res = new Response('New Response'); })

accepts() options: supports

The supports option is required and must be a string array. It defines which header values the application supports and can return.

AcceptHeader type definition

The AcceptHeader type is a union of: 'Accept', 'Accept-Charset', 'Accept-Encoding', 'Accept-Language', 'Accept-Patch', 'Accept-Post', 'Accept-Ranges'. These are the valid header names that the accepts() function can process.

accepts() options: header

The header option is required and must be of type AcceptHeader. It specifies which Accept header to examine.

accepts() options: match

The match option is optional and takes a custom function with signature (accepts: Accept[], config: acceptsConfig) => string. It allows custom logic for selecting which accepted value to return.

accepts() helper for handling Accept headers

The accepts() function from 'hono/accepts' examines the Accept header of HTTP requests and returns the appropriate value based on configuration. It takes a context object and an options object to determine which accepted value to return.

accepts() options: default

The default option is required and must be a string. It specifies the value to return when no matching supported value is found in the Accept header.

accepts() example with Accept-Language

Example showing accepts() usage: app.get('/', (c) => { const accept = accepts(c, { header: 'Accept-Language', supports: ['en', 'ja', 'zh'], default: 'en', }) return c.json({ lang: accept }) }). This handler examines the Accept-Language header and returns 'en' if none of the supported languages match.

createMiddleware() with parameters

To create middleware that accepts parameters, wrap createMiddleware() in a function that returns the middleware. Example: const messageMiddleware = (message: string) => { return createMiddleware(async (c, next) => { await next(); c.res.headers.set('X-Message', message) }) }. Then call it as app.use(messageMiddleware('Good evening!')).

createMiddleware() function

createMiddleware() is a shortcut for factory.createMiddleware() that creates custom middleware. It takes an async function receiving context and next parameters. Example: createMiddleware(async (c, next) => { await next(); c.res.headers.set('X-Message', 'Good morning!') }).

Third-party middleware packages

Third-party middleware packages are available for Hono including GraphQL Server, Firebase Authentication, and Sentry.

Built-in middleware and helpers

Hono includes the following built-in middleware and helpers: Basic Authentication, Bearer Authentication, Body Limit, Cache, Compress, Context Storage, Cookie, CORS, ETag, html, JSX, JWT Authentication, Logger, Language, Pretty JSON, Secure Headers, SSG, and Streaming.

ETag and logger middleware example

Example of adding ETag and request logging middleware to a Hono application: import { Hono } from 'hono' import { etag } from 'hono/etag' import { logger } from 'hono/logger' const app = new Hono() app.use(etag(), logger())

Bearer Auth Middleware import

Import the Bearer Auth Middleware using: import { bearerAuth } from 'hono/bearer-auth'

Bearer Auth MessageFunction type

MessageFunction is defined as (c: Context) => string | object | Promise<string | object>. It is used for wwwAuthenticateHeader and message properties in error customization options.

Bearer Auth middleware options

Bearer Auth middleware accepts the following options: - token (required): string | string[] - The string or array of strings to validate the incoming bearer token against. - realm (optional): string - The domain name of the realm for the WWW-Authenticate challenge header. Default is "". - prefix (optional): string - The prefix (schema) for the Authorization header value. Default is "Bearer". - headerName (optional): string - The header name. Default is "Authorization". - hashFunction (optional): Function - A function to handle hashing for safe comparison of authentication tokens. - verifyToken (optional): (token: string, c: Context) => boolean | Promise<boolean> - Function to verify the token. - noAuthenticationHeader (optional): object - Customizes error response when request lacks authentication header. Contains wwwAuthenticateHeader and message properties, each accepting string | object | MessageFunction. - invalidAuthenticationHeader (optional): object - Customizes error response when authentication header format is invalid. Contains wwwAuthenticateHeader and message properties. - invalidToken (optional): object - Customizes error response when token is invalid. Contains wwwAuthenticateHeader and message properties.

Bearer Auth custom token verification

Use the verifyToken option to implement custom token verification logic. The verifyToken function receives the token string and Context object, and must return boolean or Promise<boolean>. Returning true means the token is accepted.

Bearer Auth with multiple tokens

Pass an array of strings to the token option to allow multiple valid tokens: bearerAuth({ token: ['token1', 'token2'] })

Bearer Auth basic usage

To use Bearer Auth middleware, pass an object with a token property to bearerAuth(). The token can be a string or array of strings. Example: app.use('/api/*', bearerAuth({ token: 'honoiscool' }))

Bearer Auth token regex validation

The bearer token must match the regex /[A-Za-z0-9._~+/-]+=*/, otherwise a 400 error is returned. This regex accommodates both URL-safe Base64- and standard Base64-encoded JWTs. The middleware does not require the bearer token to be a JWT, just that it matches this regex.

basicAuth onAuthSuccess callback

The onAuthSuccess callback is invoked after successful authentication. It receives the context and username as parameters. This allows setting context variables without re-parsing the Authorization header. Example: onAuthSuccess: (c, username) => { c.set('username', username) }

basicAuth multiple users from config

Multiple users can be passed from a config by using spread operators. Example: app.use('/auth/*', basicAuth({ realm: 'www.example.com', ...users[0] }, ...users.slice(1))). This allows defining the first user with additional options while spreading remaining users.

basicAuth multiple users example

Example of defining multiple users: app.use('/auth/*', basicAuth({ username: 'hono', password: 'acoolproject', realm: 'www.example.com' }, { username: 'hono-admin', password: 'super-secure' }, { username: 'hono-user-1', password: 'a-secret' }))

basicAuth multiple users definition

Multiple users can be defined by passing additional objects to basicAuth as separate arguments. Each object defines a username and password pair. Options like realm can only be defined in the first object argument.

basicAuth options table

basicAuth middleware options: username (required): string. The username of the user who is authenticating. password (required): string. The password value for the provided username to authenticate against. realm (optional): string. The domain name of the realm, as part of the returned WWW-Authenticate challenge header. Default is 'Secure Area'. hashFunction (optional): Function. A function to handle hashing for safe comparison of passwords. verifyUser (optional): (username: string, password: string, c: Context) => boolean | Promise<boolean>. The function to verify the user. invalidUserMessage (optional): string | object | MessageFunction where MessageFunction is (c: Context) => string | object | Promise<string | object>. The custom message if the user is invalid. onAuthSuccess (optional): (c: Context, username: string) => void | Promise<void>. A callback function invoked after successful authentication. ...users (optional): { username: string, password: string }[]. Arbitrary parameters containing objects defining additional username and password pairs.

basicAuth verifyUser example

Example of using verifyUser: app.use(basicAuth({ verifyUser: (username, password, c) => { return username === 'dynamic-user' && password === 'hono-password' } }))

basicAuth verifyUser option

The verifyUser option is a function that takes (username, password, c) as parameters and returns a boolean or Promise<boolean>. Returning true means authentication is accepted. This allows custom verification logic instead of hardcoded credentials.

basicAuth on specific route and method

basicAuth can be applied to a specific HTTP method and route by passing it as middleware to a route handler. Example: app.delete('/auth/page', basicAuth({ username: 'hono', password: 'acoolproject' }), (c) => { return c.text('Page deleted') }).

basicAuth basic usage with username and password

The basicAuth middleware can be applied to a path using app.use() with an object containing username and password properties. Example: app.use('/auth/*', basicAuth({ username: 'hono', password: 'acoolproject' })). This restricts access to the specified path and its sub-paths.

basicAuth middleware import

Import the basicAuth middleware from 'hono/basic-auth'. The Hono app is imported from 'hono'.

bodyLimit middleware basic example

const app = new Hono() app.post( '/upload', bodyLimit({ maxSize: 50 * 1024, // 50kb onError: (c) => { return c.text('overflow :(', 413) }, }), async (c) => { const body = await c.req.parseBody() if (body['file'] instanceof File) { console.log(`Got file sized: ${body['file'].size}`) } return c.text('pass :)') } )

bodyLimit middleware behavior

The bodyLimit middleware first checks the Content-Length header in the request if present. If the header is not set, it reads the body in the stream and executes the error handler if the body is larger than the specified maximum file size.

bodyLimit middleware options

The bodyLimit middleware accepts an options object with the following properties: maxSize (number, required): The maximum file size in bytes. Default is 100 * 1024 (100kb). onError (OnError, optional): The error handler to be invoked if the specified file size is exceeded. Receives context c as a parameter.

bodyLimit middleware import

Import bodyLimit from 'hono/body-limit'.

Cache middleware Cloudflare Workers example

Example for Cloudflare Workers: ```ts app.get( '*', cache({ cacheName: 'my-app', cacheControl: 'max-age=3600', }) ) ```

Cache middleware Deno example

Example for Deno runtime: ```ts app.get( '*', cache({ cacheName: 'my-app', cacheControl: 'max-age=3600', wait: true, }) ) ``` The wait option must be set to true for Deno.

Cache middleware runtime support

The Cache middleware supports Cloudflare Workers projects using custom domains, Deno projects using Deno 1.26 or later, and Deno Deploy. Cloudflare Workers respects the Cache-Control header and returns cached responses. Deno does not respect Cache-Control headers, so custom cache update mechanisms must be implemented if needed.

Cache middleware QUERY request support

The Cache middleware caches responses to QUERY requests as required by RFC 10008. The cache key for QUERY requests includes a digest of the request content and representation metadata, so requests with different bodies are cached separately. QUERY requests with body size larger than maxQueryBodySize (64 KiB by default) bypass the cache. Cached entries are stored under internal keys of the form `/.hono/cache?__hono_cache_key=...` instead of the request URL, so cache API calls to caches.delete() with the original request URL will not delete QUERY cached entries.

Cache middleware import

Import the cache middleware with `import { cache } from 'hono/cache'`. Also import Hono with `import { Hono } from 'hono'`.

Cache middleware cacheableStatusCodes example

Example of caching specific status codes: ```ts app.get( '*', cache({ cacheName: 'my-app', cacheControl: 'max-age=3600', cacheableStatusCodes: [200, 404, 412], }) ) ```

Cache middleware onCacheNotAvailable suppress logging example

Example of suppressing onCacheNotAvailable logging: ```ts app.use( cache({ cacheName: 'my-app-v1', onCacheNotAvailable: false, }) ) ```

Cache middleware options

Cache middleware options: cacheName (required, string | (c: Context) => string | Promise<string>): the name of the cache store; wait (optional, boolean, default false): whether to wait for cache.put Promise to resolve (required true for Deno); cacheControl (optional, string): Cache-Control header directives; vary (optional, string | string[]): sets Vary header, values merged with existing Vary header, cannot be '*'; keyGenerator (optional, (c: Context) => string | Promise<string>, default c.req.url): generates cache keys; maxQueryBodySize (optional, number, default 65536): maximum QUERY request body size in bytes to cache, larger requests bypass cache; cacheableStatusCodes (optional, number[], default [200]): array of status codes to cache; onCacheNotAvailable (optional, ((reason: string) => void | Promise<void>) | false, default logs with console.log): callback when Cache API not available or QUERY caching cannot use Web Crypto, or false to suppress.

Cache middleware onCacheNotAvailable custom logging example

Example of custom onCacheNotAvailable callback: ```ts app.use( cache({ cacheName: 'my-app-v1', onCacheNotAvailable: () => { console.log('Custom log: Cache API is not available.') }, }) ) ```

Combine Middleware example with some() and every()

Example showing complex access control using combine middleware: ```ts import { Hono } from 'hono' import { bearerAuth } from 'hono/bearer-auth' import { getConnInfo } from 'hono/cloudflare-workers' import { every, some } from 'hono/combine' import { ipRestriction } from 'hono/ip-restriction' import { rateLimit } from '@/my-rate-limit' const app = new Hono() app.use( '*', some( every( ipRestriction(getConnInfo, { allowList: ['192.168.0.2'] }), bearerAuth({ token }) ), // If both conditions are met, rateLimit will not execute. rateLimit() ) ) app.get('/', (c) => c.text('Hello Hono!')) ``` This example shows using some() to apply either the combined ipRestriction and bearerAuth middleware, or rateLimit middleware as an alternative.

every() - run all middleware

The every() function runs all given middleware in order and stops if any of them fail. If any middleware throws an error, subsequent middleware will not run.

some() - run first successful middleware

The some() function runs middleware in order and stops after the first one that returns true without error. If any middleware exits successfully, subsequent middleware will not run. Useful for applying an alternative middleware if the first one fails.

Combine Middleware import

Import combine middleware functions from 'hono/combine'. The module exports three functions: some, every, and except.

every() example with local network check

Example using every() combined with some() for complex access control: ```ts import { some, every } from 'hono/combine' import { bearerAuth } from 'hono/bearer-auth' import { myCheckLocalNetwork } from '@/check-local-network' import { myRateLimit } from '@/rate-limit' // If client is in local network, skip authentication and rate limiting. // Otherwise, apply authentication and rate limiting. app.use( '/api/*', some( myCheckLocalNetwork(), every(bearerAuth({ token }), myRateLimit({ limit: 100 })) ) ) ```

except() - run middleware conditionally

The except() function runs all given middleware except when a condition is met. It accepts a string or function as the condition. If multiple targets need to be matched, pass them as an array.

except() example with public endpoints

Example using except() to skip middleware for specific routes: ```ts import { except } from 'hono/combine' import { bearerAuth } from 'hono/bearer-auth' // If client is accessing public API, skip authentication. // Otherwise, require a valid token. app.use('/api/*', except('/api/public/*', bearerAuth({ token }))) ```

some() example with bearerAuth and rateLimit

Example using some() to conditionally apply middleware based on authentication: ```ts import { some } from 'hono/combine' import { bearerAuth } from 'hono/bearer-auth' import { myRateLimit } from '@/rate-limit' // If client has a valid token, skip rate limiting. // Otherwise, apply rate limiting. app.use( '/api/*', some(bearerAuth({ token }), myRateLimit({ limit: 100 })) ) ```

Give your agent this brain