Repository navigation
feat: Add WebSocket support for single-app deployments - #893
Conversation
One deployment now serves the site, the HTTP API and WebSockets, Vercel
included. Nitro owns `/api/ws`; each handshake runs through the embedded
Hono API (Origin check, rate limiter, session), and the connection uses the
same `handleVitNodeWebSocket` protocol as the standalone API via the new
`@vitnode/core/ws/crossws.server` adapter.
- Reconnects restore state: `useVitNodeWebSocket` returns a `connectionId`
that changes on every open, in leader and follower tabs; live editing
rejoins its room on it.
- Redis: handshakes answer 503 until the instance is subscribed; a
resubscribe after an outage closes local sockets with 1012 so clients
reload missed state; publishes are kept alive with the request's
`waitUntil`.
- `vitnode({ webSocket: true })` turns the browser connection on in an app
that embeds the API; the single-app template ships the route.
- The WebSocket Origin check also accepts the handshake's own origin.
- Docs: WebSocket setup and reconnects pages; Vercel page no longer says
WebSockets are unsupported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SkDZQZ5eahXVkPDEnVB5P1
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
React Doctor found 7 issues in 6 files · 7 warnings · score 77 / 100 (Needs work) · vs 7 warnings
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f043d11d54
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .catch(() => { | ||
| // Realtime stays single-instance; local clients still get their messages. | ||
| }) |
There was a problem hiding this comment.
Retry failed Redis subscriptions without awaiting reconnect
If the initial SUBSCRIBE command rejects while the Redis connection remains open, this catch only swallows the error and leaves subscribed false. The ready event denotes the connection becoming ready and is not re-emitted for an ordinary command failure (node-redis connection docs), so subscribe() is never called again; every subsequent /api/ws handshake then waits five seconds and returns 503 until the process or Redis connection restarts. Schedule another subscription attempt or deliberately reconnect after this failure.
Useful? React with 👍 / 👎.
| @@ -37,6 +43,8 @@ const sendNotificationState = async ( | |||
| userId: number, | |||
| ): Promise<void> => { | |||
| const state = await getNotificationState(c.get("db"), userId); | |||
There was a problem hiding this comment.
React Doctor · react-doctor/async-defer-await (warning)
This await blocks the function before an early-return that doesn't use the awaited value, so the skip path waits for nothing. Move the await below the guard so it only runs when you need the data
Fix → Move the await below the early-return guard so the skip path stays fast and avoids unnecessary async work.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a47addc6b1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }) | ||
| ``` | ||
|
|
||
| A web app pointed at that API with `VITNODE_API_URL` connects to it on its own; it needs no `webSocket` option and no Nitro route. |
There was a problem hiding this comment.
Fix automatic connection for split-host APIs
This setup claim fails for a common production configuration such as web https://www.example.com/ and API https://api.example.com/: shouldConnectWebSocket() compares only pathname and port, so with both URLs using / and the default HTTPS port it returns false and the provider never opens the socket. Compare the origins/hostnames as well, or document the additional configuration required for split-host deployments.
Useful? React with 👍 / 👎.
Description
What?
This PR adds full WebSocket support for single-app deployments (Nitro-based apps that embed the VitNode API), bringing feature parity with standalone API servers. Previously, WebSockets were only available when the API ran separately.
Key additions:
createVitNodeWebSocketHooks: New crossws adapter that bridges Nitro's WebSocket handler to VitNode's protocol, running handshakes through the embedded API so they get the same session, Origin checks, and middleware as standalone APIsupgradeWebSocket: Hono helper for the/wsroute that coordinates with the crossws hookswaitForRealtimePubSub()to delay handshakes until Redis subscription is ready, preventing clients from missing messages after reconnectsconnectionIdthat changes on reconnect, allowing clients to detect state loss and restore rooms/reload dataWhy?
Single-app deployments are the default for new VitNode projects. WebSocket support completes the feature set for this deployment model, enabling real-time features like live editing, notifications, and collaborative tools without requiring a separate API server. The implementation reuses VitNode's existing protocol and handlers, ensuring consistent behavior across deployment types.
The connection ID mechanism solves a critical issue: clients need to know when to restore state after a reconnect, since pub/sub has no replay and rooms are lost. This is especially important for features like live editing that depend on accurate room membership.
Realtime readiness checking prevents a race condition in serverless environments where a socket could open before Redis subscription completes, causing missed messages.
https://claude.ai/code/session_01SkDZQZ5eahXVkPDEnVB5P1