Published Build3 min read
Meteor 3.5 puts a registry where the SockJS weld used to be
The DDP WebSocket transport is now pluggable, and uWebSockets.js is one env var away. SockJS stays the default, the benchmark story is admitted to be uneven, and the sharp edges are all in configuration.
Written for builders.See today for builders

What happened
- For thirteen years every DDP message a Meteor app sent rode SockJS; server-side that was a copy-fork of SockJS v0.3.4 (vintage 2012), carried forward release after release because it worked and because the transport was tightly integrated with the rest of the DDP stack.
- Meteor 3.5 makes the DDP WebSocket transport pluggable: there is a transport registry in packages/ddp-server/transports/index.js and SockJS is just one implementation behind it.
- SockJS stays the default transport; existing apps behave exactly as before and nothing changes unless you ask.
- A different transport can be selected with a single environment variable: DDP_TRANSPORT=uws meteor run.
- uws is uWebSockets.js, the Node.js binding for uWebSockets, a WebSocket server written in C/C++.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
Meteor 3.5 has made the DDP WebSocket transport pluggable: SockJS now sits behind a transport registry in `packages/ddp-server/transports/index.js`, and teams can select uWebSockets.js instead [2]. That matters because for thirteen years every DDP message rode a copy-fork of SockJS v0.3.4, vintage 2012, carried release to release and tightly integrated with the rest of the DDP stack [1] - what the Meteor post on dev.to calls load-bearing duct tape: fast enough, compatible everywhere, completely unswappable [17].
The opt-in is `DDP_TRANSPORT=uws meteor run` [4]. `uws` is uWebSockets.js, the Node binding for uWebSockets, a WebSocket server written in C/C++ [5]. SockJS remains the default and existing apps behave exactly as before [3], which means an upgrade to 3.5 changes nothing in the transport path until someone sets a switch [16].
The client-side consequence is the more interesting half. Select a non-SockJS transport and the browser stops loading the SockJS shim at runtime, opening a native `WebSocket` straight to `/websocket` with no 2012-era fallback negotiation in the path [6]. That removes a layer from the connection handshake rather than just swapping a server library.
Three switches select the transport, and they resolve in a fixed order: a `packages.ddp-server.transport` entry in settings.json wins, then `DDP_TRANSPORT`, then the legacy `DISABLE_SOCKJS=1` alias; with nothing set you get sockjs [7]. Operators should read that precedence as a footgun: a settings file committed months ago quietly overrides the env var you just exported. The settings path is also the only way to set the uws internal port, host, payload length and timeout [10], so even on the env-var route you still need `METEOR_SETTINGS` for a non-default port [8]. Meteor prints no transport line at startup [9], which is why the post's verification loop is an `echo` method plus a client log of `__meteor_runtime_config__.DDP_TRANSPORT` [15].
Two more traps in the getting-started path. The pluggable transport ships in 3.5, so scaffold with `meteor create --release 3.5` or run `meteor update --release 3.5`; a plain `meteor create` uses whatever the installed CLI defaults to, which may still be 3.4.x [11]. And the default 3.5 skeleton is the React one, whose client entry is `client/main.jsx` pinned in `package.json` under `meteor.mainModule.client`; drop a new `client/main.js` next to it and Meteor silently ignores it with no error [13]. You do not need to add `ddp-server` to `.meteor/packages` - it arrives transitively via `meteor-base` and shows up in `.meteor/versions` [12].
On performance, the author is upfront rather than promotional: the post's own outline promises the real benchmark numbers, "the good and the meh", alongside the caveats you need before shipping [14]. The excerpt available here stops before those figures [18], so treat the speed case as unproven until you read them or run your own.
What to watch: whether the published benchmarks show a win on message throughput, on connection count, or only on synthetic echo loops [14]; whether the caveats section names proxy, load balancer or sticky-session behaviour, since removing SockJS fallback negotiation removes a compatibility crutch [6]; and whether the default ever flips away from SockJS, which is the only change that would force the rest of the ecosystem to care [3].
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
For thirteen years every DDP message a Meteor app sent rode SockJS; server-side that was a copy-fork of SockJS v0.3.4 (vintage 2012), carried forward release after release because it worked and because the transport was tightly integrated with the rest of the DDP stack.
- [2]
Meteor 3.5 makes the DDP WebSocket transport pluggable: there is a transport registry in packages/ddp-server/transports/index.js and SockJS is just one implementation behind it.
- [3]
SockJS stays the default transport; existing apps behave exactly as before and nothing changes unless you ask.
- [4]
A different transport can be selected with a single environment variable: DDP_TRANSPORT=uws meteor run.
- [5]
uws is uWebSockets.js, the Node.js binding for uWebSockets, a WebSocket server written in C/C++.
- [6]
When a non-SockJS transport is selected, the browser stops using the SockJS shim at runtime and connects with native WebSocket, new WebSocket(...) straight to /websocket, with no 2012-era fallback negotiation in the path.
Sources & coverage · 1 publisher
The reporting this story was synthesized from, earliest first. Every link goes to the original.
- dev.toitalojsAug 12Meteor pluggable DDP transport: meet uWebSockets.js
Cited in this coverage: Meteor post on dev.to
Additional citations
- sourcing note on the supplied excerpt

