Bun

TCP Sockets

Use Bun's native TCP API implement performance sensitive systems like database clients, game servers, or anything that needs to communicate over TCP (instead of HTTP). This is a low-level API intended for library authors and for advanced use cases.

Start a server

To start a TCP server with Bun.listen:

Bun.listen({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {}, // message received from client
    open(socket) {}, // socket opened
    close(socket) {}, // socket closed
    drain(socket) {}, // socket ready for more data
    error(socket, error) {}, // error handler
  },
});

An API designed for speed

Contextual data can be attached to a socket in the open handler.

type SocketData = { sessionId: string };

Bun.listen<SocketData>({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {
      socket.write(`${socket.data.sessionId}: ack`);
    },
    open(socket) {
      socket.data = { sessionId: "abcd" };
    },
  },
});

To enable TLS, pass a tls object containing keyFile and certFile properties.

Bun.listen({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, data) {},
  },
  tls: {
    certFile: "cert.pem",
    keyFile: "key.pem",
  },
});

The result of Bun.listen is a server that conforms to the TCPSocket instance.

const server = Bun.listen({
  /* config*/
});

// stop listening
// parameter determines whether active connections are closed
server.stop(true);

// let Bun process exit even if server is still listening
server.unref();

Create a connection

Use Bun.connect to connect to a TCP server. Specify the server to connect to with hostname and port. TCP clients can define the same set of handlers as Bun.listen, plus a couple client-specific handlers.

// The client
const socket = Bun.connect({
  hostname: "localhost",
  port: 8080,

  socket: {
    data(socket, data) {},
    open(socket) {},
    close(socket) {},
    drain(socket) {},
    error(socket, error) {},

    // client-specific handlers
    connectError(socket, error) {}, // connection failed
    end(socket) {}, // connection closed by server
    timeout(socket) {}, // connection timed out
  },
});

To require TLS, specify tls: true.

// The client
const socket = Bun.connect({
  // ... config
  tls: true,
});

Hot reloading

Both TCP servers and sockets can be hot reloaded with new handlers.

Server
Client
Server
const server = Bun.listen({ /* config */ })

// reloads handlers for all active server-side sockets
server.reload({
  socket:
    data(){
      // new 'data' handler
    }
  }
})
Client
const socket = Bun.connect({ /* config */ })
socket.reload({
  data(){
    // new 'data' handler
  }
})

Buffering

Currently, TCP sockets in Bun do not buffer data. For performance-sensitive code, it's important to consider buffering carefully. For example, this:

socket.write("h");
socket.write("e");
socket.write("l");
socket.write("l");
socket.write("o");

...performs significantly worse than this:

socket.write("hello");

To simplify this for now, consider using Bun's ArrayBufferSink with the {stream: true} option:

const sink = new ArrayBufferSink({ stream: true, highWaterMark: 1024 });

sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");

queueMicrotask(() => {
  var data = sink.flush();
  if (!socket.write(data)) {
    // put it back in the sink if the socket is full
    sink.write(data);
  }
});

Corking — Support for corking is planned, but in the meantime backpressure must be managed manually with the drain handler.