Skip to content
// EFFECT DAYS 2026

BunHttpServer unstable

Bun implementation of the Effect HttpServer.

make creates a scoped HTTP server from Bun.serve, converting Bun Request values into HttpServerRequest values and Effect HttpServerResponse values back into Web Response values. The server supports streaming bodies, multipart requests, file responses through BunHttpPlatform, and WebSocket upgrades. Bun.serve cannot hand over the connection for other protocols, so other upgrades fail. This module also provides layers for the server alone, the Bun HTTP support services, the combined server, configurable server options, and a test server with an HTTP client.

8 exports Since v4.0.0 Source

Constructors

make

unstable
Since v4.0.0

Creates a scoped Bun HttpServer from Bun.serve options, stopping the server on scope finalization with optional graceful shutdown settings.

Details

idleTimeout defaults to 0, disabling Bun's 10 second idle timeout, so quiet response streams and slow uploads are bounded only by the Stream and Effect timeouts in your handlers or middleware. An explicit value is passed to Bun unchanged.

On shutdown the server waits up to gracefulShutdownTimeout (default 20 seconds) for in-flight requests, then interrupts the remaining request fibers. disablePreemptiveShutdown: true skips the wait and interrupts them immediately. Handlers run uninterruptibly unless wrapped in Effect.interruptible or served by interruptible HttpRouter routes, so a client disconnect does not stop them and shutdown waits for them to finish.

Gotchas

Without an idle timeout, Bun also keeps idle keep-alive connections and clients that stopped reading a response open, and handler timeouts cannot catch either. Directly exposed servers should set idleTimeout (seconds, at most 255) or sit behind a proxy with its own idle timeout.

Signature

declare const make: <R extends string>(...args: [options: any]) => Effect<{
readonly address: SocketAddress;
readonly serve: {
<E, R>(effect: Effect<HttpServerResponse, E, R>): Effect<void, never, Scope | Exclude<R, HttpServerRequest>>;
<E, R, App extends Effect<HttpServerResponse, any, any>>(effect: Effect<HttpServerResponse, E, R>, middleware: Applied<App, E, R>): Effect<void, never, Scope | Exclude<Services<App>, HttpServerRequest>>;
};
}, ServeError, Scope>

Layers

layer

unstable
Since v4.0.0

Layer that provides a Bun HttpServer together with the Bun HTTP platform, ETag generator, and Bun services.

Details

idleTimeout defaults to 0 (disabled); see make.

Signature

declare function layer<R extends string>(options: any): Layer<HttpPlatform | Generator | BunServices | HttpServer, ServeError>

layerConfig

unstable
Since v4.0.0

Creates the Bun HTTP server and support-services layer from configurable serve options.

Signature

declare function layerConfig<R extends string>(options: Config<any>): Layer<HttpPlatform | Generator | FileSystem | Path | HttpServer, ServeError | ConfigError>
Since v4.0.0

Layer that provides Bun HTTP support services: HttpPlatform, weak ETag generation, and BunServices.

Signature

declare const layerHttpServices: Layer.Layer<HttpPlatform | Etag.Generator | BunServices.BunServices>

layerServer

unstable
Since v4.0.0

Layer that provides only HttpServer by constructing a scoped Bun server from the supplied serve options.

Signature

declare const layerServer: <R extends string>(options: ServeOptions<R> & {
readonly disablePreemptiveShutdown?: boolean;
readonly gracefulShutdownTimeout?: Duration.Input;
readonly websocket?: WebSocketOptions;
}) => Layer.Layer<Server.HttpServer, Error.ServeError>

Options

ServeOptions

type unstable
Since v4.0.0

Bun serve options accepted by the HTTP server, extended with typed route definitions.

Signature

type ServeOptions<R extends string> = Bun.Serve.UnixServeOptions<WebSocketContext> | Bun.Serve.HostnamePortServeOptions<WebSocketContext> & {
readonly routes?: Bun.Serve.Routes<WebSocketContext, R>;
}

WebSocketOptions

type unstable
Since v4.0.0

WebSocket tuning options forwarded to Bun.serve's websocket handler.

Details

The lifecycle handlers (open, message, close, ...) are managed by the server and cannot be overridden; everything else — such as perMessageDeflate compression, payload limits, and idle timeouts — passes through, e.g. BunHttpServer.layer({ port: 3000, websocket: { perMessageDeflate: true } }).

The compressionThreshold option controls the minimum message size in bytes that is compressed when per-message deflate is negotiated. It defaults to 1024, matching the default threshold of Node's ws server.

Signature

type WebSocketOptions = Omit<Bun.WebSocketHandler<WebSocketContext>, "open" | "message" | "close" | "drain" | "ping" | "pong" | "data" | "binaryType"> & {
readonly compressionThreshold?: number;
}

Testing

layerTest

unstable
Since v4.0.0

Layer that starts a Bun HTTP server on an ephemeral port for tests.

Signature

declare const layerTest: Layer.Layer<Server.HttpServer | HttpPlatform | FileSystem.FileSystem | Etag.Generator | Path.Path | HttpClient>