Ensuring efficient and seamless communication between the client and server is key when building modern, real-time web applications. Traditional HTTP requests—like those used in polling—are stateless and one-directional. The client makes a request (e.g., using fetch or axios) to the server, and the server responds before the connection is closed. If the client needs fresh data, it must repeatedly send new requests at regular intervals, creating unnecessary latency and increasing load on both the client and server.
For example, if you're building a live chat app or a stock price tracker, polling would require the client to request updates every second or so, even when there’s no new data to fetch. This is where WebSockets shine.
WebSockets provide a persistent, two-way communication channel between the client and the server. Once the connection is established, the server can instantly push updates to the client without waiting for a new request. This makes WebSockets ideal for scenarios where real-time updates are essential, such as:
Using Vanilla JavaScript on the client side and the Bun runtime on the server side makes implementing WebSockets straightforward and efficient. For example:
In this scenario, WebSockets offer lower latency, reduced server load, and a smoother user experience than traditional polling methods.
First, ensure the Bun is installed. Then create a new Bun project, create a new empty directory, enter into the new directory, and initialize the project via the bun init command:
mkdir websocket-demo cd websocket-demo bun init
The bun init command will create the package.json file, a "hello world" index.ts file, the .gitignore file, the tsconfig.json file for the typescript configuration, and a README.md file.
Now, you can start creating your JavaScript code. I'm going to show you the whole script; then we will explore all the relevant parts. You can edit the index.ts file:
console.log("? Hello via Bun! ?"); const server = Bun.serve({ port: 8080, // defaults to $BUN_PORT, $PORT, $NODE_PORT otherwise 3000 fetch(req, server) { const url = new URL(req.url); if (url.pathname === "/") return new Response(Bun.file("./index.html")); if (url.pathname === "/surprise") return new Response("?"); if (url.pathname === "/chat") { if (server.upgrade(req)) { return; // do not return a Response } return new Response("Upgrade failed", { status: 400 }); } return new Response("404!"); }, websocket: { message(ws, message) { console.log("✉️ A new Websocket Message is received: " + message); ws.send("✉️ I received a message from you: " + message); }, // a message is received open(ws) { console.log("? A new Websocket Connection"); ws.send("? Welcome baby"); }, // a socket is opened close(ws, code, message) { console.log("⏹️ A Websocket Connection is CLOSED"); }, // a socket is closed drain(ws) { console.log("DRAIN EVENT"); }, // the socket is ready to receive more data }, }); console.log(`? Server (HTTP and WebSocket) is launched ${server.url.origin}`);
Below is a breakdown of the provided code, explaining each part and its functionality.
mkdir websocket-demo cd websocket-demo bun init
The Bun.serve method initializes a server capable of handling both HTTP and WebSocket requests.
console.log("? Hello via Bun! ?"); const server = Bun.serve({ port: 8080, // defaults to $BUN_PORT, $PORT, $NODE_PORT otherwise 3000 fetch(req, server) { const url = new URL(req.url); if (url.pathname === "/") return new Response(Bun.file("./index.html")); if (url.pathname === "/surprise") return new Response("?"); if (url.pathname === "/chat") { if (server.upgrade(req)) { return; // do not return a Response } return new Response("Upgrade failed", { status: 400 }); } return new Response("404!"); }, websocket: { message(ws, message) { console.log("✉️ A new Websocket Message is received: " + message); ws.send("✉️ I received a message from you: " + message); }, // a message is received open(ws) { console.log("? A new Websocket Connection"); ws.send("? Welcome baby"); }, // a socket is opened close(ws, code, message) { console.log("⏹️ A Websocket Connection is CLOSED"); }, // a socket is closed drain(ws) { console.log("DRAIN EVENT"); }, // the socket is ready to receive more data }, }); console.log(`? Server (HTTP and WebSocket) is launched ${server.url.origin}`);
The websocket key defines event handlers to manage WebSocket connections.
const server = Bun.serve({ port: 8080, // defaults to $BUN_PORT, $PORT, $NODE_PORT otherwise 3000 ... });
Triggered when a client establishes a WebSocket connection.
fetch(req, server) { const url = new URL(req.url); if (url.pathname === "/") return new Response(Bun.file("./index.html")); if (url.pathname === "/surprise") return new Response("?"); if (url.pathname === "/chat") { if (server.upgrade(req)) { return; // do not return a Response } return new Response("Upgrade failed", { status: 400 }); } return new Response("404!"); }
Triggered when the server receives a message from the client.
open(ws) { console.log("? A new Websocket Connection"); ws.send("? Welcome baby"); }
Triggered when a WebSocket connection is closed.
Parameters:
message(ws, message) { console.log("✉️ A new Websocket Message is received: " + message); ws.send("✉️ I received a message from you: " + message); }
The drain event is triggered when the WebSocket is ready to accept more data after being temporarily overwhelmed.
close(ws, code, message) { console.log("⏹️ A Websocket Connection is CLOSED"); }
Logs the server's URL to the console once it's running.
Once you have your index.ts file, you can start the server via bun run:
drain(ws) { console.log("DRAIN EVENT"); }
The server is ready and up and running. Now, we can implement the client.
Now we understand the structure of the script for handling the WebSocket, the next steps are:
The above is the detailed content of WebSocket with JavaScript and Bun. For more information, please follow other related articles on the PHP Chinese website!