Overview
Introduction
WebSockets enable real-time, bi-directional communication channels between a client and a server over a single TCP connection. Hono provides built-in WebSocket helper functions that abstract away runtime-specific differences, letting you handle connection lifecycles and message events smoothly across different server environments.
Note
WebSocket support depends heavily on the runtime environment hosting your Hono application. Ensure you import the upgradeWebSocket helper from the correct runtime-specific adapter package.
Supported Runtimes and Adapters
Hono provides dedicated WebSocket upgrade adapters for specific JavaScript runtimes. The following table lists the supported adapters and their respective import paths:
| Runtime | Adapter Import Path |
|---|---|
| Cloudflare Workers | hono/cloudflare-workers |
| Deno | hono/deno |
| Bun | hono/bun |
Sources: src/adapter/cloudflare-workers/websocket.ts, src/adapter/deno/websocket.ts, src/adapter/bun/websocket.ts
Setting Up WebSocket Routes
Configuring the Endpoint
To establish a WebSocket connection in a Hono application, use the upgradeWebSocket helper within a route handler. You can define event listeners such as onOpen, onMessage, onClose, and onError to govern the behavior of the connection.
The example below demonstrates how to configure a WebSocket endpoint that echoes incoming messages back to the client:
import { Hono } from 'hono'
import { upgradeWebSocket } from 'hono/cloudflare-workers' // or 'hono/deno', 'hono/bun'
const app = new Hono()
app.get(
'/ws',
upgradeWebSocket((c) => {
return {
onOpen: (event, ws) => {
console.log('Connection opened')
},
onMessage: (event, ws) => {
console.log(`Received message: ${event.data}`)
ws.send(event.data)
},
onClose: (event, ws) => {
console.log('Connection closed')
},
onError: (event, ws) => {
console.error('An error occurred', event)
},
}
})
)
export default appTip
Cloudflare Workers do not natively support the onOpen event listener in their WebSocket implementation. If you target Cloudflare Workers, omit onOpen from your event definitions.
Key Concepts
To effectively manage WebSockets in Hono, familiarize yourself with the following core abstractions:
upgradeWebSocket: A middleware helper function that inspects incoming HTTP requests for upgrade headers, negotiates subprotocols, and transitions the HTTP connection into a persistent WebSocket stream.WSContext: A unified wrapper class providing runtime-agnostic methods and properties to interact with the active WebSocket connection. ThroughWSContext, you can send data, inspect connection states, or terminate the session.WSReadyState: Numeric codes representing the state of the WebSocket connection (0for connecting,1for open,2for closing, and3for closed).
Controlling Connections with WSContext
The WSContext instance passed to every event listener provides several methods and properties to manage the socket session:
ws.send(data, options): Sends text or binary data (string,ArrayBuffer, orUint8Array) back to the client. You can pass an optional{ compress: true }parameter if supported by the runtime.ws.close(code, reason): Gracefully terminates the WebSocket connection with an optional numeric status code and text reason.ws.readyState: Returns the current numeric ready state of the socket.ws.url: Returns theURLobject associated with the connection request.ws.protocol: Returns the negotiated subprotocol string, if any.ws.raw: Exposes the underlying runtime-specific native WebSocket object for advanced use cases.
Client-Side Integration
When consuming Hono applications with the Hono RPC client (hc), you can establish a WebSocket connection directly from the client proxy using the .ws() method.
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('http://localhost:8787/')
// Establish a WebSocket connection
const ws = client.ws.$ws()
ws.onopen = () => {
ws.send('Hello from client!')
}
ws.onmessage = (event) => {
console.log('Message from server:', event.data)
}Sources: src/client/client.ts
Warning
Always verify that your reverse proxy or load balancer (such as Nginx, Cloudflare, or AWS API Gateway) is configured to permit and persist WebSocket upgrade headers (Upgrade: websocket and Connection: Upgrade), otherwise connection attempts will fail with handshake errors.