Hono Third-Party Middleware

repository·main·Indexed 21 days ago

https://github.com/honojs/middleware

A monorepo of third-party middleware for the Hono web framework published under the @hono npm namespace. Includes packages such as @hono/ajv-validator and @hono/arktype-validator for request validation, @hono/auth-js for authentication with React support, @hono/bun-transpiler for serving TS/TSX files in Bun, and @hono/capnweb for Cap'n Web RPC API integration across Cloudflare Workers, Node.js, and Deno.

Tokens
108.6K
Snippets
384
Records
442
Agent score
76%

What's inside honojs/middleware

  1. Use TypeDriver validator middleware for Hono

    main

    The @hono/typedriver-validator middleware provides unified validation for Hono applications using TypeDriver. It supports three schema formats: TypeScript DSL, JSON Schema, and Standard Schema (e.g., Zod). You can validate different request parts like json, query, param, or header by specifying the target in the first argument.

    import { tdValidator } from '@hono/typedriver-validator'
    
    // Example usage with TypeScript DSL
    const route = app.post(
      '/user',
      tdValidator(
        'json',
        `{
      name: string
      age: number
    }`
      ),
      (c) => {
        const user = c.req.valid('json')
        return c.json({ success: true, message: `${user.name} is ${user.age}` })
      }
    )
  2. Important considerations for esbuild Transpiler Middleware

    main

    Caching

    This middleware does not have a built-in cache feature. Every request for a TypeScript/TSX file will trigger a transpilation. To improve performance, you should wrap this in Hono Cache Middleware or implement your own caching logic.

    Vite Compatibility

    @hono/vite-dev-server does not support Wasm, so this middleware cannot be used with it. If you are using Vite, it is recommended to let Vite handle the transpilation instead.

  3. Configure Casbin model and policy

    main

    Casbin requires a model configuration file (model.conf) and a policy file (policy.csv) to define authorization rules.

    Example model.conf structure: Defines request, policy, effect, and matcher logic.

    Example policy.csv structure: Maps subjects (users) to objects (paths) and actions (HTTP methods).

    For detailed policy writing, refer to the official Casbin documentation.

    [request_definition]
    r = sub, obj, act
    
    [policy_definition]
    p = sub, obj, act
    
    [policy_effect]
    e = some(where (p.eft == allow))
    
    [matchers]
    m = r.sub == p.sub && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")
    p, alice, /dataset1/*, *
    p, bob, /dataset1/*, GET
  4. Use Partial Reloads and Lazy Function Props

    main

    Inertia partial reloads allow fetching only a subset of props. @hono/inertia handles the X-Inertia-Partial-* headers automatically.

    To optimize performance, use function props (() => T | Promise<T>). These are evaluated lazily: they are only executed if the client specifically requests them in a partial reload. If a standard full visit occurs, they are resolved normally.

    app.get('/dashboard', (c) =>
      c.render('Dashboard', {
        user, // sent every time
        stats: () => db.heavyQuery(), // function prop — only invoked when included
      })
    )
  5. Avoid memory leaks when using event handlers in middleware

    main

    When adding event handlers inside Hono middleware or request handlers, do not use anonymous functions or closures.

    Because middleware runs on every request, creating an anonymous function inside it instructs the emitter to add a new, unique handler object to memory every time the request is processed. Since these functions cannot be checked for equality, they cannot be removed, leading to memory leaks and duplicate handler execution.

    Best Practice: Use named functions if you need to use the on() method inside middleware.

  6. Understand OpenTelemetry instrumentation limitations

    main
    The @hono/otel middleware instruments the entire request-response lifecycle. It does not provide fine-grained instrumentation for individual middleware components within your Hono application; it treats the middleware stack as a single unit of work for the span.
  7. How @hono/oidc-auth works

    main

    The @hono/oidc-auth middleware provides storage-less login sessions using OpenID Connect (OIDC). It manages the authentication lifecycle as follows:

    1. Session Check: It checks for a session cookie.
    2. Redirection: If no cookie exists, the user is redirected to the Identity Provider (IdP) authentication endpoint.
    3. Callback & Exchange: After IdP authentication, the user is redirected back to the application. The middleware exchanges the authorization code for a refresh token and generates a signed JWT session cookie.
    4. Verification: The JWT is signed with a symmetric key and verified at the edge to prevent tampering.
    5. Implicit Refresh: After a refresh interval (default 15 minutes), the middleware uses the refresh token to verify authentication with the IdP and regenerates the session cookie.
    6. Expiration: If the session expires (default 1 day), the refresh token is revoked and the user is redirected to the IdP for re-authentication.
    # Concept: Storage-less OIDC sessions
    # 1. User visits app -> No cookie found
    # 2. Middleware redirects to IdP
    # 3. User logs in at IdP -> Redirected to /callback
    # 4. Middleware exchanges code for tokens -> Sets JWT cookie
    # 5. Subsequent requests use JWT cookie for auth
  8. Security Best Practices for Session Cookies

    main

    When using Firebase session cookies in a web application, follow these security guidelines to mitigate common attacks:

    1. CSRF (Cross-Site Request Forgery): Use Hono's built-in csrf() middleware to protect your routes.
    2. XSS (Cross-Site Scripting):
      • Set the httpOnly flag on your cookies to prevent JavaScript from accessing them.
      • Implement a strong Content Security Policy (CSP) using Hono's secure-headers middleware.
    3. MitM (Man-in-the-middle):
      • Use secure: true to ensure cookies are only sent over HTTPS.
      • Use sameSite: 'Strict' or sameSite: 'Lax'.
      • Consider using __Secure- or __Host- cookie prefixes.

    Recommended Cookie Settings:

    const secureCookieSettings: CookieOptions = {
      path: '/',
      domain: <your_domain>,
      secure: true,
      httpOnly: true,
      sameSite: 'Strict',
    }
  9. Understand the Emitter lifecycle and scope

    main

    The Event Emitter is not request-scoped. A single Emitter instance is shared across all incoming requests.

    This design choice is intentional to:

    1. Prevent memory leaks (especially when handlers use closures or large data structures).
    2. Reduce strain on the JavaScript garbage collector by avoiding the constant creation and destruction of emitter instances per request.
  10. Access Twitch OAuth data via c.get

    main

    After the twitchAuth flow, the following data is available in the Hono context via c.get():

    • token: Object with token (string) and expires_in (number).
    • refresh-token: Object with token (string) and expires_in (number).
    • granted-scopes: Array of string[].
    • user-twitch: Object containing user info (id, login, display_name, email, etc.).
    app.get('/twitch', (c) => {
      const token = c.get('token')
      const refreshToken = c.get('refresh-token')
      const grantedScopes = c.get('granted-scopes')
      const user = c.get('user-twitch')
    
      return c.json({
        token,
        refreshToken,
        grantedScopes,
        user,
      })
    })