@hono/node-server

repository·main·Indexed 20 days ago

https://github.com/honojs/node-server

A Node.js adapter for the Hono web framework that allows running Hono applications in a Node.js environment (version > 20.x) by utilizing Node's web standard API implementations. It provides utilities for serving static files via serveStatic, handling WebSockets with upgradeWebSocket, sending HTTP 103 Early Hints, and accessing low-level connection details through getConnInfo. The adapter includes a lightweight Request and Response implementation to maintain compatibility with the Fetch API while integrating with Node.js IncomingMessage and ServerResponse.

Tokens
9.9K
Snippets
39
Records
43
Agent score
69%

What's inside @hono/node-server

  1. Understand @hono/node-server benchmark metrics

    main

    The benchmark tests three specific endpoint types under a load of 500 concurrent connections for 10 seconds, measuring Requests per second (Reqs/sec):

    1. Ping (GET /): Measures simple response handling.
    2. Query (GET /id/:id): Measures path parameter and query parameter handling.
    3. Body (POST /json): Measures JSON body processing.

    Interpreting the Results Table

    ColumnDescription
    npmPerformance of the published @hono/node-server package.
    devPerformance of the local development version (built from the repository dist/ directory).
    DifferenceThe percentage change. Positive values indicate an improvement in the dev version; negative values indicate a regression.
  2. Enable WebSocket support

    main

    To use WebSockets, install the ws package and @types/ws. Use upgradeWebSocket from @hono/node-server to handle upgrades and provide a WebSocketServer instance in the serve options.

    import { serve, upgradeWebSocket } from '@hono/node-server'
    import { WebSocketServer } from 'ws'
    import { Hono } from 'hono'
    
    const app = new Hono()
    
    app.get(
      '/ws',
      upgradeWebSocket(() => ({
        onMessage(event, ws) {
          ws.send(event.data)
        },
      }))
    )
    
    const wss = new WebSocketServer({ noServer: true })
    serve({
      fetch: app.fetch,
      websocket: { server: wss },
    })
  3. Basic Usage of @hono/node-server

    main

    Import serve from @hono/node-server to start your Hono application. The code remains compatible with Cloudflare Workers, Deno, and Bun.

    import { serve } from '@hono/node-server'
    import { Hono } from 'hono'
    
    const app = new Hono()
    app.get('/', (c) => c.text('Hono meets Node.js'))
    
    serve(app, (info) => {
      console.log(`Listening on http://localhost:${info.port}`)
    })
  4. Run performance benchmarks for @hono/node-server

    main

    Use the benchmark suite to compare the performance of the published npm version of @hono/node-server against the local development version. The benchmark measures raw performance of the adapter using a basic Fetch API-based application (without the Hono framework) to isolate the adapter's overhead.

    Prerequisites

    Execution Steps

    Run the following commands from the repository root:

    pnpm install
    pnpm run -w build
    pnpm run benchmark
  5. Use HttpBindings and Http2Bindings in FetchCallback

    main

    The FetchCallback receives a standard Web API Request and an env object containing the underlying Node.js primitives. The shape of env depends on whether you are using HTTP/1.1 or HTTP/2:

    • HttpBindings (HTTP/1.1):

      • incoming: IncomingMessage from node:http
      • outgoing: ServerResponse from node:http
    • Http2Bindings (HTTP/2):

      • incoming: Http2ServerRequest from node:http2
      • outgoing: Http2ServerResponse from node:http2
    import type { FetchCallback, HttpBindings } from '@hono/node-server'
    
    const fetch: FetchCallback = async (request, env) => {
      // env is HttpBindings if using HTTP/1.1
      const { incoming, outgoing } = env as HttpBindings;
      // ...
      return new Response('Hello Hono!');
    }
  6. Understand `WSContext` in Hono WebSockets

    main

    The WSContext is the object passed to your WebSocket event handlers (like onOpen, onMessage, etc.). It acts as a wrapper around the raw WebSocket instance, providing a standardized interface.

    Key properties and methods:

    • send(source, opts?): Sends data to the client. opts can include { compress: boolean }.
    • close(code?, reason?): Closes the connection with a specific status code and reason.
    • binaryType: Set to 'arraybuffer'.
    • protocol: The sub-protocol used.
    • readyState: The current state of the connection.
    • url: The URL of the request.
    • raw: The underlying raw WebSocket instance (e.g., the ws object).
  7. Configure WebSocket support in the Node.js adapter

    main

    To use WebSockets with the Hono Node.js adapter, you must provide a WebSocket server in the options.websocket object.

    Critical Requirement: When passing a WebSocket server (such as one from the ws package), it must be created with the { noServer: true } option. If this option is not set, the adapter will throw an error to prevent conflicts with the standard HTTP server lifecycle.

    When configured, setupWebSocket integrates the WebSocket server with the Hono fetch callback.

    import { serve } from '@hono/node-server'
    import { Hono } from 'hono'
    import { WebSocketServer } from 'ws'
    
    const app = new Hono()
    const wss = new WebSocketServer({ noServer: true })
    
    serve({
      fetch: app.fetch,
      websocket: {
        server: wss
      }
    })
  8. Use `upgradeWebSocket` for WebSocket support in Node.js

    main

    When using @hono/node-server, you can implement WebSocket functionality by using the upgradeWebSocket helper. This helper integrates Hono's WebSocket API with the underlying Node.js server and a WebSocket library (like ws).

    It provides a WSContext which allows you to interact with the connection via send(), close(), and access properties like readyState, protocol, and url. It also handles standard WebSocket events: onOpen, onMessage, onClose, and onError.

    Note that upgradeWebSocket relies on the UpgradeBindings being present in the environment, which is typically handled by the setupWebSocket function during server initialization.

    import { Hono } from 'hono'
    import { upgradeWebSocket } from '@hono/node-server/websocket'
    
    const app = new Hono()
    
    app.get('/ws', upgradeWebSocket((c, ws) => {
      ws.on('message', (message) => {
        console.log('Message received:', message)
        ws.send('Hello from Hono!')
      })
      
      ws.on('close', (event, ctx) => {
        console.log('Connection closed:', event.code, event.reason)
      })
    }))
    
    // Note: You must also call setupWebSocket with your server and wss instance
    // as part of your server startup logic to bridge the upgrade process.
  9. Enable precompressed content support

    main

    To serve precompressed assets (like .gz or .br files), set precompressed: true in your serveStatic options. The middleware will check the client's Accept-Encoding header and attempt to serve the corresponding compressed file if it exists on disk. It automatically sets the Content-Encoding and Vary: Accept-Encoding headers.

    app.use('/static/*', serveStatic({
      root: './public',
      precompressed: true
    }))
  10. Access Node.js APIs via c.env

    main

    You can access Node.js-specific objects like IncomingMessage and ServerResponse through c.env. Use the HttpBindings type for type safety.

    Bindings Types:

    • HttpBindings: Contains incoming: IncomingMessage and outgoing: ServerResponse.
    • Http2Bindings: Contains incoming: Http2ServerRequest and outgoing: Http2ServerResponse.
    import { serve } from '@hono/node-server'
    import type { HttpBindings } from '@hono/node-server'
    import { Hono } from 'hono'
    
    const app = new Hono<{ Bindings: HttpBindings }>()
    
    app.get('/', (c) => {
      return c.json({
        remoteAddress: c.env.incoming.socket.remoteAddress,
      })
    })
    
    serve(app)
  11. Use Early Hints middleware

    main

    Import earlyHints from @hono/node-server/early-hints to send HTTP 103 Early Hints. This allows browsers to preload resources. The middleware only sends hints for requests that look like document navigations (checked via Sec-Fetch-Mode or Sec-Fetch-Dest).

    Supports both static link strings and dynamic link functions.

    import { serve } from '@hono/node-server'
    import { earlyHints } from '@hono/node-server/early-hints'
    import { Hono } from 'hono'
    
    const app = new Hono()
    
    // Static links
    app.use(earlyHints({ link: '</styles.css>; rel=preload; as=style' }))
    
    // Dynamic links
    app.use(earlyHints({
      link: (c) => c.req.query('theme') === 'dark' 
        ? '</dark.css>; rel=preload; as=style' 
        : '</light.css>; rel=preload; as=style'
    }))
    
    serve(app)