Skip to content

Allow enabling websocket compression in NodeHttpServer and BunHttpServer - #6691

Merged
tim-smart merged 2 commits into
Effect-TS:mainfrom
t3dotgg:feat/websocket-server-options
Jul 28, 2026
Merged

Allow enabling websocket compression in NodeHttpServer and BunHttpServer#6691
tim-smart merged 2 commits into
Effect-TS:mainfrom
t3dotgg:feat/websocket-server-options

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

tl;dr - I want to add compression to websockets in T3 Code. It will help cut data transferred on T3 Code by as much as 80%

Happy to talk more about this. Figured the patch was simple enough to file a PR quick - no hard feelings if this gets closed :)

---- FROM HERE DOWN IS AI SLOP ----

Browsers offer permessage-deflate on every WebSocket connection, but an Effect HTTP server has no way to accept it. NodeHttpServer hardcodes new WebSocketServer({ noServer: true }), and BunHttpServer overwrites any websocket key in the serve options with its own handlers — so there is no way to enable compression or tune payload limits without patching the packages.

For apps streaming JSON over websockets this leaves a lot on the table: measured on real production traffic (small ~500B-1KB frames with repeated keys and UUIDs, raw TCP byte counts), accepting permessage-deflate with context takeover cuts wire bytes by 4x.

Solution

Both servers accept an optional websocket field in their existing options, forwarded to the underlying implementation. The wiring/lifecycle options the server manages (noServer, open/message/close, etc.) are excluded from the type so they cannot be overridden.

// Node: forwarded to the ws WebSocketServer
NodeHttpServer.layer(() => createServer(), {
  port: 3000,
  websocket: { perMessageDeflate: true }
})

// Bun: merged into Bun.serve's websocket handler
BunHttpServer.layer({
  port: 3000,
  websocket: { perMessageDeflate: true }
})

Omitting the option keeps today's behavior exactly; clients that don't offer the extension keep uncompressed frames on their connection.

Includes a test asserting the server accepts permessage-deflate when configured and offered, and that clients not offering it still connect uncompressed. tsc -b on both packages, oxlint, dprint, and jsdocs are clean; a changeset is included.

🤖 Drafted with Claude Code (Fable 5), reviewed and filed by a human.

Summary by CodeRabbit

  • New Features
    • Added a configurable websocket option to both Node.js and Bun HTTP servers.
    • Enables forwarding WebSocket configuration (for example, per-message deflate settings) while keeping connection lifecycle behavior managed by the servers.
  • Bug Fixes
    • Preserved and applied user-provided WebSocket settings instead of overriding them with fixed defaults.
  • Tests
    • Added coverage to verify WebSocket option forwarding, including per-message compression behavior.
  • Documentation
    • Updated release documentation with usage examples for Node.js and Bun.

…erver

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog Jul 28, 2026
@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 51c31fb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 29 packages
Name Type
@effect/platform-node Patch
@effect/platform-bun Patch
effect Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-deno Patch
@effect/platform-node-shared Patch
@effect/vitest Patch
@effect/ai-anthropic Patch
@effect/ai-openai-compat Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/docgen Patch
@effect/openapi-generator Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@effect-slopcop effect-slopcop Bot added 4.0 enhancement New feature or request labels Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ec8d08e-8abb-4209-86e1-2aaef56af7ca

📥 Commits

Reviewing files that changed from the base of the PR and between 71e24ca and 51c31fb.

📒 Files selected for processing (3)
  • .changeset/http-server-websocket-options.md
  • packages/platform-bun/src/BunHttpServer.ts
  • packages/platform-node/src/NodeHttpServer.ts
💤 Files with no reviewable changes (1)
  • .changeset/http-server-websocket-options.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/platform-node/src/NodeHttpServer.ts
  • packages/platform-bun/src/BunHttpServer.ts

📝 Walkthrough

Walkthrough

NodeHttpServer and BunHttpServer now accept optional websocket configuration, forward supported settings to their WebSocket implementations, and retain internal lifecycle handling. Node tests verify perMessageDeflate negotiation, and a changeset documents the new capability.

Changes

WebSocket options

Layer / File(s) Summary
Node WebSocket option contract and forwarding
packages/platform-node/src/NodeHttpServer.ts, packages/platform-node/test/NodeHttpServer.test.ts
Adds the shared Options interface, forwards supported websocket settings to NodeWS.WebSocketServer, propagates the type through server layers, and tests permessage-deflate negotiation.
Bun WebSocket option contract and forwarding
packages/platform-bun/src/BunHttpServer.ts, .changeset/http-server-websocket-options.md
Adds WebSocketOptions, forwards configuration through Bun server constructors and layers, and documents the Node and Bun websocket options.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WebSocketClient
  participant NodeHttpServer
  participant NodeWS.WebSocketServer
  WebSocketClient->>NodeHttpServer: Connect to /ws with extension settings
  NodeHttpServer->>NodeWS.WebSocketServer: Create server with websocket options and noServer
  NodeWS.WebSocketServer-->>WebSocketClient: Negotiate permessage-deflate extension
Loading
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/platform-node/src/NodeHttpServer.ts`:
- Around line 65-85: Update the WebSocketServer creation flow in NodeHttpServer
to explicitly remove port and server from runtime options.websocket before
forwarding them, while retaining the locally managed noServer setting. Ensure
only non-conflicting WebSocket options are passed to NodeWS.Server.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b90fcd19-0fae-4d1b-a93b-dcbe4ee81a99

📥 Commits

Reviewing files that changed from the base of the PR and between 45e7810 and 71e24ca.

📒 Files selected for processing (4)
  • .changeset/http-server-websocket-options.md
  • packages/platform-bun/src/BunHttpServer.ts
  • packages/platform-node/src/NodeHttpServer.ts
  • packages/platform-node/test/NodeHttpServer.test.ts

Comment thread packages/platform-node/src/NodeHttpServer.ts
@github-project-automation github-project-automation Bot moved this from Discussion Ongoing to Waiting on Author in PR Backlog Jul 28, 2026
Comment thread .changeset/http-server-websocket-options.md
*/
export type WebSocketOptions = Omit<
Bun.WebSocketHandler<WebSocketContext>,
"open" | "message" | "close" | "drain" | "ping" | "pong" | "data"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also drop "binaryType" here too, so it can't be set to arraybuffer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 51c31fbbinaryType added to the omit. Good call: the server's message handler feeds Socket.fromWebSocket, which expects the default nodebuffer framing, so letting callers flip it to arraybuffer would break message decoding.

Comment thread packages/platform-node/src/NodeHttpServer.ts
Comment on lines +70 to +73
* `websocket` is forwarded to the underlying `ws` `WebSocketServer`, minus the
* wiring options the server manages itself. Use it to enable
* `permessage-deflate` compression or tune payload limits, e.g.
* `NodeHttpServer.layer(() => createServer(), { port: 3000, websocket: { perMessageDeflate: true } })`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have this comment inline with the property or comment on all the property keys.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved inline onto the websocket property in 51c31fb. Left the neighboring shutdown options as they were since they predate this PR.

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

File Name Current Size Previous Size Difference
basic.ts 6.63 KB 6.63 KB 0.00 KB (0.00%)
batching.ts 9.37 KB 9.37 KB 0.00 KB (0.00%)
brand.ts 6.26 KB 6.26 KB 0.00 KB (0.00%)
cache.ts 10.12 KB 10.12 KB 0.00 KB (0.00%)
config.ts 19.19 KB 19.19 KB 0.00 KB (0.00%)
differ.ts 18.42 KB 18.42 KB 0.00 KB (0.00%)
http-client.ts 20.88 KB 20.88 KB 0.00 KB (0.00%)
logger.ts 10.28 KB 10.28 KB 0.00 KB (0.00%)
metric.ts 8.55 KB 8.55 KB 0.00 KB (0.00%)
optic.ts 7.41 KB 7.41 KB 0.00 KB (0.00%)
pubsub.ts 14.20 KB 14.20 KB 0.00 KB (0.00%)
queue.ts 11.09 KB 11.09 KB 0.00 KB (0.00%)
schedule.ts 10.27 KB 10.27 KB 0.00 KB (0.00%)
schema-class.ts 18.16 KB 18.16 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 28.02 KB 28.02 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 24.34 KB 24.34 KB 0.00 KB (0.00%)
schema-string-transformation.ts 12.69 KB 12.69 KB 0.00 KB (0.00%)
schema-string.ts 10.35 KB 10.35 KB 0.00 KB (0.00%)
schema-template-literal.ts 14.49 KB 14.49 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 20.93 KB 20.93 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 23.38 KB 23.38 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 18.32 KB 18.32 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 18.00 KB 18.00 KB 0.00 KB (0.00%)
schema-toFormatter.ts 17.88 KB 17.88 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 21.52 KB 21.52 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 18.56 KB 18.56 KB 0.00 KB (0.00%)
schema.ts 17.43 KB 17.43 KB 0.00 KB (0.00%)
stm.ts 12.05 KB 12.05 KB 0.00 KB (0.00%)
stream.ts 9.37 KB 9.37 KB 0.00 KB (0.00%)

@moishinetzer moishinetzer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small change, otherwise LGTM. I'm griping with the fact that shovelling down a config to the constructor feels like its not taking advantage of effect by creating a service with a default layer for the config but this is small enough that I'll leave it to you or @tim-smart whether that makes sense here.

…naryType from Bun options

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@fubhy
fubhy requested a review from tim-smart July 28, 2026 11:51
@tim-smart
tim-smart merged commit 6a5e86f into Effect-TS:main Jul 28, 2026
15 checks passed
@github-project-automation github-project-automation Bot moved this from Waiting on Author to Done in PR Backlog Jul 28, 2026
t3dotgg added a commit to pingdotgg/t3code that referenced this pull request Jul 28, 2026
The websocket options passthrough was upstreamed as Effect-TS/effect#6691
and ships in @effect/platform-{node,bun} >= 4.0.0-beta.103. The comments
mark both patches as deletable on that upgrade, replaced by the layer
option websocket: { perMessageDeflate } in apps/server/src/server.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants