T
TanStack•3mo ago
other-emerald

How to use websocket in the latest alpha version?

I saw devinxi is merged, but how to use websocket in the latest alpha version, are there any guides?
10 Replies
conscious-sapphire
conscious-sapphire•3mo ago
As far as I can tell there's no way currently of setting up websockets as nitro config isn't able to be set through the plugin any more Additionally the auto-generated nitro types no longer include the event handlers so even if you set up a websocket handler though nitro.config.ts it won't have any types or imports
other-emerald
other-emeraldOP•3mo ago
I see, thank you for your insight
frozen-sapphire
frozen-sapphire•3mo ago
https://github.com/nick22985/tanstsackstart-socketio which starts a socket.io server however now trying to figure out how to get that contect inside the serverFn. This only works in dev
stormy-gold
stormy-gold•3mo ago
This is bad news, I was hoping that it would become easier to access the Nitro server not harder. Improved WebSocket support was something we were waiting for. 😢
stormy-gold
stormy-gold•3mo ago
Here's a simple adapter that should let you create crossws WebSocket handlers with createServerFileRoute: https://gist.github.com/darkobits/4b2073742af7d89707e216915fae7e9d
Gist
WebSocket Adapter for TanStack Start
WebSocket Adapter for TanStack Start. GitHub Gist: instantly share code, notes, and snippets.
typical-coral
typical-coral•3mo ago
Hey @Joshua where do I add this in the project? Never mind I scrolled past your comment above the code
typical-coral
typical-coral•3mo ago
@Joshua did that solution work for you? It never successfully connects for me
No description
stormy-gold
stormy-gold•3mo ago
Yes. • I'm on the latest version of Start/Router (^1.121.21) • The endpoint with the WebSocket handler is at src/routes/api/ws.ts • I'm able to connect from the client and exchange messages successfully. Handler:
import { createServerFileRoute } from '@tanstack/react-start/server'
import { createWebSocketHandler } from '@/lib/crossws-adapters/tanstack-start'

export const ServerRoute = createServerFileRoute('/api/ws').methods(createWebSocketHandler({
hooks: {
message: (peer, msg) => {
console.log('Server got message:', Buffer.from(msg).toString('utf8'))
peer.send('This is a message from the server.')
}
}
}))
import { createServerFileRoute } from '@tanstack/react-start/server'
import { createWebSocketHandler } from '@/lib/crossws-adapters/tanstack-start'

export const ServerRoute = createServerFileRoute('/api/ws').methods(createWebSocketHandler({
hooks: {
message: (peer, msg) => {
console.log('Server got message:', Buffer.from(msg).toString('utf8'))
peer.send('This is a message from the server.')
}
}
}))
Test component:
import React from 'react'

export function WebSocketDemo() {
React.useEffect(() => {
const ws = new WebSocket(`ws://${location.host}/api/ws`)

ws.addEventListener('open', () => {
ws.send('This is a message from the client.')
})

ws.addEventListener('message', event => {
console.log('[WebSocket] Got message from server:', event.data)
})

ws.addEventListener('error', event => {
console.error('[WebSocket] Error:', event)
})

return () => {
// Note: This will cause an error in development because React will call
// this effect twice, closing the first connection when it does.
ws.close()
}
}, [])
}
import React from 'react'

export function WebSocketDemo() {
React.useEffect(() => {
const ws = new WebSocket(`ws://${location.host}/api/ws`)

ws.addEventListener('open', () => {
ws.send('This is a message from the client.')
})

ws.addEventListener('message', event => {
console.log('[WebSocket] Got message from server:', event.data)
})

ws.addEventListener('error', event => {
console.error('[WebSocket] Error:', event)
})

return () => {
// Note: This will cause an error in development because React will call
// this effect twice, closing the first connection when it does.
ws.close()
}
}, [])
}
This doesn't require any additional Nitro config (ie: setting experimental.websockets: true). My TanStack Start config (now in vite.config.ts) is completely empty.
sunny-green
sunny-green•2mo ago
This doesn't work with ^1.125 or 1.121.21
rare-sapphire
rare-sapphire•2mo ago
came up with this, its a hack but whatever its working for me
import * as Bun from "bun";
import {
websocketHandler as wsCallbacks,
fetchHandler as wsFetch,
} from "./src/chat/server.ts";


const dev = process.argv[2] === "dev";
// run on seperate port in development, since we still use vite dev which doesnt have a entrypoint
if(dev){

Bun.serve({
port: 3001,
fetch: wsFetch,
websocket: wsCallbacks,
});
// dont run the rest, we just sleep forever
await new Promise(() => {});
}


let capturedServeOpts: any;

const BunServe = Bun.serve;

(Bun as any).serve = function (opts: any): Bun.Server {
capturedServeOpts = opts;
// nitro doesnt use the returned server, so we're fine.
return { port: opts.port } as unknown as Bun.Server;
};

// import the generated server
//@ts-ignore
await import("./.output/server/index.mjs");

if (!capturedServeOpts || typeof capturedServeOpts.fetch !== "function") {
throw new Error(
"Failed to capture Nitro fetch handler from generated output"
);
}


(Bun as any).serve = BunServe;

Bun.serve({
port: capturedServeOpts.port,
websocket: wsCallbacks,
async fetch(req, server) {
const url = new URL(req.url);

//dedicated endpoint
if (url.pathname === "/api/ws") {
return wsFetch(req, server);
}

// Delegate everything else to Nitro
return capturedServeOpts!.fetch!(req, server);
},
});
import * as Bun from "bun";
import {
websocketHandler as wsCallbacks,
fetchHandler as wsFetch,
} from "./src/chat/server.ts";


const dev = process.argv[2] === "dev";
// run on seperate port in development, since we still use vite dev which doesnt have a entrypoint
if(dev){

Bun.serve({
port: 3001,
fetch: wsFetch,
websocket: wsCallbacks,
});
// dont run the rest, we just sleep forever
await new Promise(() => {});
}


let capturedServeOpts: any;

const BunServe = Bun.serve;

(Bun as any).serve = function (opts: any): Bun.Server {
capturedServeOpts = opts;
// nitro doesnt use the returned server, so we're fine.
return { port: opts.port } as unknown as Bun.Server;
};

// import the generated server
//@ts-ignore
await import("./.output/server/index.mjs");

if (!capturedServeOpts || typeof capturedServeOpts.fetch !== "function") {
throw new Error(
"Failed to capture Nitro fetch handler from generated output"
);
}


(Bun as any).serve = BunServe;

Bun.serve({
port: capturedServeOpts.port,
websocket: wsCallbacks,
async fetch(req, server) {
const url = new URL(req.url);

//dedicated endpoint
if (url.pathname === "/api/ws") {
return wsFetch(req, server);
}

// Delegate everything else to Nitro
return capturedServeOpts!.fetch!(req, server);
},
});

Did you find this page helpful?