Skip to content

Commit 69752b7

Browse files
chore(release): router crates and artifacts (#1386)
1 parent 048d07b commit 69752b7

16 files changed

Lines changed: 394 additions & 193 deletions

.changeset/add_websocket_connection_reuse_and_execution_mode_configuration.md

Lines changed: 0 additions & 71 deletions
This file was deleted.

.changeset/align_variable_coercion_errors_with_graphql_js.md

Lines changed: 0 additions & 10 deletions
This file was deleted.

.changeset/fix_prometheus_counter_total_total_suffix.md

Lines changed: 0 additions & 14 deletions
This file was deleted.

.changeset/mask_internal_pipeline_errors.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

.changeset/multiplex_and_reuse_websocket_subgraph_connections.md

Lines changed: 0 additions & 51 deletions
This file was deleted.

.changeset/propagate_all_set_cookie_values_from_a_single_subgraph.md

Lines changed: 0 additions & 12 deletions
This file was deleted.

.changeset/upgrade_laboratory_to_latest_v024.md

Lines changed: 0 additions & 10 deletions
This file was deleted.

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/router/CHANGELOG.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,165 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
116116
### Other
117117

118118
- *(deps)* update release-plz/action action to v0.5.113 ([#389](https://github.com/graphql-hive/router/pull/389))
119+
## 0.0.89 (2026-08-12)
120+
121+
### Features
122+
123+
#### Multiplex and reuse WebSocket subgraph connections
124+
125+
The router can now multiplex GraphQL operations over shared `graphql-transport-ws` subgraph connections.
126+
127+
Subscriptions with the same subgraph and inbound connection identity reuse one initialized connection instead of opening one WebSocket per operation. Different operations retain independent streams while sharing the physical connection.
128+
129+
Queries and mutations can also reuse a connection opened by a subscription:
130+
131+
```yaml
132+
subscriptions:
133+
enabled: true
134+
websocket:
135+
subgraphs:
136+
reviews:
137+
path: /reviews/ws
138+
139+
traffic_shaping:
140+
all:
141+
websocket:
142+
reuse_connections: true
143+
execute_mode: reuse_existing
144+
```
145+
146+
With this configuration:
147+
148+
1. A subscription initializes the pooled `reviews` connection.
149+
2. Matching subscriptions multiplex over it.
150+
3. Matching queries and mutations use it while it remains initialized.
151+
4. A query or mutation uses HTTP when the connection is missing, expired, or still initializing.
152+
153+
Use WebSocket for every operation by selecting `websocket` mode:
154+
155+
```yaml
156+
traffic_shaping:
157+
all:
158+
websocket:
159+
reuse_connections: true
160+
execute_mode: websocket
161+
```
162+
163+
The first operation initializes the connection, concurrent operations join that initialization, and later matching operations reuse the initialized connection.
164+
165+
Once an operation selects WebSocket, transport failures and timeouts are returned to the client without retrying over HTTP. This prevents a mutation that may have reached the subgraph from being executed twice.
166+
167+
Idle pooled connections close after the effective `pool_idle_timeout`. Active operations keep the connection open, and dropping one operation cancels only that operation without closing the shared connection.
168+
169+
The router also exposes WebSocket pool telemetry for active connections and operations, initialization success and failure, initialization waiters, reuse lookup hits and misses, and connection closure reasons. These metrics help measure reuse hit rate, multiplexing, connection churn, handshake failures, and per-subgraph pool usage.
170+
171+
### Fixes
172+
173+
#### Add WebSocket connection reuse and execution mode configuration
174+
175+
WebSocket-enabled subgraphs can now configure connection reuse and choose how queries and mutations are transported.
176+
177+
Configure defaults for all subgraphs under `traffic_shaping.all.websocket`:
178+
179+
```yaml
180+
subscriptions:
181+
enabled: true
182+
websocket:
183+
subgraphs:
184+
reviews:
185+
path: /reviews/ws
186+
187+
traffic_shaping:
188+
all:
189+
pool_idle_timeout: 50s # default
190+
websocket:
191+
reuse_connections: true
192+
execute_mode: reuse_existing
193+
```
194+
195+
`reuse_connections` defaults to `true`:
196+
197+
- `true` multiplexes matching operations over initialized pooled WebSocket connections
198+
- `false` opens a dedicated connection for each WebSocket operation
199+
200+
`execute_mode` defaults to `http` and supports:
201+
202+
- `http`: queries and mutations always use HTTP
203+
- `reuse_existing`: queries and mutations use an initialized matching WebSocket when available, otherwise they immediately use HTTP
204+
- `websocket`: queries and mutations use WebSocket, creating or joining a pooled connection when reuse is enabled
205+
206+
Settings can be overridden per subgraph. Omitted WebSocket fields inherit the global value:
207+
208+
```yaml
209+
traffic_shaping:
210+
all:
211+
pool_idle_timeout: 50s # default
212+
websocket:
213+
reuse_connections: true
214+
execute_mode: reuse_existing
215+
216+
subgraphs:
217+
payments:
218+
pool_idle_timeout: 5s
219+
websocket:
220+
reuse_connections: false
221+
execute_mode: websocket
222+
```
223+
224+
In this example, other WebSocket-enabled subgraphs opportunistically reuse initialized connections. `payments` sends each operation over a dedicated WebSocket.
225+
226+
Pooled WebSockets use the effective `pool_idle_timeout`. A per-subgraph value overrides `traffic_shaping.all.pool_idle_timeout` for both HTTP and WebSocket pools. Active WebSocket operations do not expire.
227+
228+
Connection matching uses the inbound headers selected by `traffic_shaping.router.dedupe.headers`, even when router request deduplication is disabled. Include every header that can affect connection-scoped authentication, authorization, cookies, or tenant identity:
229+
230+
```yaml
231+
traffic_shaping:
232+
router:
233+
dedupe:
234+
headers:
235+
include: [authorization, cookie, x-tenant]
236+
```
237+
238+
#### Improve variable coercion error messages
239+
240+
Variable coercion errors (invalid scalar/enum/object values, missing required fields, non-null violations) reports clear and informative error messages.
241+
242+
This only changes error text - error codes and HTTP status codes are unchanged.
243+
244+
#### Fix doubled `_total` suffix on Prometheus counters
245+
246+
The built-in Prometheus metrics exporter (`/metrics`) generated counter names with a
247+
doubled suffix, e.g. `hive_router_graphql_errors_total_total` instead of
248+
`hive_router_graphql_errors_total`.
249+
250+
Counter names on `/metrics` now end in a single `_total`, matching standard
251+
Prometheus conventions.
252+
253+
OTLP metrics exporter is not affected.
254+
255+
#### Mask internal error details from client responses
256+
257+
Improve router's error handling by masking internal error details from client responses.
258+
259+
Client-caused errors still return their real message, since it only ever reflects the client's own request. Internal errors now always return a generic `"Internal server error"` message and never the underlying error message, which previously leaked details such as subgraph URLs, storage/network errors, and other backend internals. The real error is still logged for debugging purposes.
260+
261+
Error codes are unchanged. HTTP status codes are unchanged, except for GraphQL operation normalization and minification failures, which are now correctly treated as router-side bugs and always return `500` (previously `400`, or `200` based on `Accept` header) instead of being treated as a client mistake.
262+
263+
#### Propagate all multi-instance headers from a single subgraph response
264+
265+
When a subgraph responded with multiple instances of a never-join header (`Set-Cookie` or `WWW-Authenticate`), the router only forwarded one of them to the client and silently dropped the rest.
266+
267+
The fix is to propagate all values of a never-join header as separate header fields end-to-end, rather than just the first value.
268+
269+
Fixes https://github.com/graphql-hive/router/issues/1388
270+
271+
#### Upgrade Laboratory to latest (`v0.2.4`)
272+
273+
Upgrade Laboratory to latest version (`@graphql-hive/laboratory@0.2.4`). This release includes the following changes:
274+
275+
- Schema documentation: browse root types, types and fields, search across the whole type map (including input objects and enum values), and read descriptions, deprecations and argument defaults.
276+
- Builder rows gain an "Open in Docs" context menu entry, and the GraphQL editor hover gains an "Open in Docs" link.
277+
119278
## 0.0.88 (2026-08-10)
120279

121280
### Features

0 commit comments

Comments
 (0)