S
SolidJS•2mo ago
Zikado

SolidStart Websocket Proxy

Hello, I've tried to create a proxy to standalone websocket server in SolidStart, for purpose of this showcase I created a simple demo showing the issue I ran into. index.js aka the websocket server
const WebSocket = require("ws");

const PORT = 8080;
const wss = new WebSocket.Server({ port: PORT });

wss.on("connection", (ws) => {
console.log("New client connected");

ws.send("Hello");

ws.on("message", (message) => {
console.log(`Received: ${message}`);
});

ws.on("close", () => {
console.log("Client disconnected");
});
});
const WebSocket = require("ws");

const PORT = 8080;
const wss = new WebSocket.Server({ port: PORT });

wss.on("connection", (ws) => {
console.log("New client connected");

ws.send("Hello");

ws.on("message", (message) => {
console.log(`Received: ${message}`);
});

ws.on("close", () => {
console.log("Client disconnected");
});
});
then I have this very simple Websocket connection on the client app.tsx
import { isServer } from "solid-js/web";

export default function App() {
if (!isServer) {
const connection = new WebSocket("ws://localhost:3000/ws");
connection.onopen = () => {
console.log("OPENED");
};
connection.onmessage = (e) => {
console.log(JSON.stringify(e.data));
};
}
return <main>Some content</main>;
}
import { isServer } from "solid-js/web";

export default function App() {
if (!isServer) {
const connection = new WebSocket("ws://localhost:3000/ws");
connection.onopen = () => {
console.log("OPENED");
};
connection.onmessage = (e) => {
console.log(JSON.stringify(e.data));
};
}
return <main>Some content</main>;
}
and here is the proxy app.config.ts
import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
vite: {
server: {
proxy: {
"/ws": {
target: "ws://localhost:8080",
rewrite: (path) => path.replace(/^\/ws/, ""),
ws: true,
rewriteWsOrigin: true,
},
},
},
},
});
import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
vite: {
server: {
proxy: {
"/ws": {
target: "ws://localhost:8080",
rewrite: (path) => path.replace(/^\/ws/, ""),
ws: true,
rewriteWsOrigin: true,
},
},
},
},
});
Now, the problem is that when trying to establish the ws connection it gets through to the server but gets closed immediately, so the console logs on the server would look like this
New client connected
Client disconnected
New client connected
Client disconnected
on the client it will take some time before eventually getting an error that the connection could not have been established. I have also tried to recreate the same situation with SvelteKit but there it worked without any problem with the same setup (just edited vite.config directly). I appreciate your help, thanks.
25 Replies
peerreynders
peerreynders•2mo ago
Have a look at this: https://github.com/peerreynders/solid-start-ws-demo One big limitation is that the web socket server is isolated from the actual SolidStart server functionality as it is running in it's own vinxi route.
GitHub
GitHub - peerreynders/solid-start-ws-demo: Use a websocket server w...
Use a websocket server within SolidStart v1. Contribute to peerreynders/solid-start-ws-demo development by creating an account on GitHub.
zulu
zulu•2mo ago
why does the vite proxy no longer works? or did it never work?
zulu
zulu•2mo ago
No description
peerreynders
peerreynders•2mo ago
I'm not sure. I just wanted to make sure that the overall limitations (web socket support being experimental) were clear up front. My next question would be: Do the proxy options work through vinxi for regular http requests (and to what extent)? They may, they may not. All of these type of issues will likely be addressed as SolidStart pivots over to taking advantage of Nitro more.
Zikado
ZikadoOP•2mo ago
Thanks for the answers, however I'm not really sure what to take away from them. My intention was to run this solid app in serverless environment hence it's not suitable as websocket server for my implementation. That's why I wanted to create the proxy to the standalone server. Do you think that would be feasible? Thank you again.
zulu
zulu•2mo ago
you don't need a proxy when you deploy, you just need a websocket that allow your domain to access it CORS wise even locally you can just run your websocket on a different port and skip the whole proxy thing
Zikado
ZikadoOP•2mo ago
Ohh, okay, I just thought that it would be better not to directly expose the ws server, but to be honest I'm not particularly sure how much of a big deal it is.
peerreynders
peerreynders•2mo ago
Have you tried making connection a module global? I wonder if the connection is being disposed once the App component has finished setup.
zulu
zulu•2mo ago
it does not make sense sorry the ws connection is open what do you have to hide?
Zikado
ZikadoOP•2mo ago
Yeah you're probably right, sorry about the stupid questions
zulu
zulu•2mo ago
the question is not stupid, but you will need some good use case to use a more complex setup than you actually need. simple solutions are usually better any proxying will probably also cost you more
Zikado
ZikadoOP•2mo ago
Okay, thank you for clarifying. I'll will just connect to it directly then 🙂
zulu
zulu•2mo ago
yeah sound like a plan, when you deploy the websocket server set the cors headers to restrict access from origins you allow. in development you can just set it to any to make things easier
Zikado
ZikadoOP•2mo ago
Thanks for the advice, I'll keep that in mind. I have tried to do that hopefully the way you meant it
import { isServer } from "solid-js/web";

let connection;

if (!isServer) {
connection = new WebSocket("ws://localhost:3000/ws");
connection.onopen = () => {
console.log("OPENED");
};
connection.onmessage = (e) => {
console.log(JSON.stringify(e.data));
};
}

export default function App() {
console.log(connection);
setTimeout(() => {
console.log(connection);
}, 2000);
return <main>Some content</main>;
}
import { isServer } from "solid-js/web";

let connection;

if (!isServer) {
connection = new WebSocket("ws://localhost:3000/ws");
connection.onopen = () => {
console.log("OPENED");
};
connection.onmessage = (e) => {
console.log(JSON.stringify(e.data));
};
}

export default function App() {
console.log(connection);
setTimeout(() => {
console.log(connection);
}, 2000);
return <main>Some content</main>;
}
it behave the same way as before, the connection reference was still available, sorry if I got it wrong
zulu
zulu•2mo ago
are you saying that things still don't work?
Zikado
ZikadoOP•2mo ago
Yes
zulu
zulu•2mo ago
check the devtools / network panel, see if you are getting COR error on the ws connection or any error for that matter
Zikado
ZikadoOP•2mo ago
in the network panel it just gets stuck on "pending" and in firefox I eventually get that: Firefox was not able to establish connection with ws://localhost:3000/ws. That's it.
zulu
zulu•2mo ago
do you have a differnt browser to try with also try with a simple websocket client to test the server
Zikado
ZikadoOP•2mo ago
I have tried that on Firefox and Brave, the server works well when connecting directly to it (meaning using the server url directly without the proxy)
zulu
zulu•2mo ago
did you try it now or before?
Zikado
ZikadoOP•2mo ago
Now and also before, the result was the same
zulu
zulu•2mo ago
but we are no longer using the proxy so why will it not connect? is the ws server listening on port 3000 or 8080?
Zikado
ZikadoOP•2mo ago
ohh sorry, the thing I wrote regarded this message
Have you tried making connection a module global?

I wonder if the connection is being disposed once the App component has finished setup.
Have you tried making connection a module global?

I wonder if the connection is being disposed once the App component has finished setup.
so I juse tried doing it this way (still with the proxy). Without the proxy it works like a charm. (sorry if I mislead you)
peerreynders
peerreynders•2mo ago
That said once you have a working dev version, try to build it and see if it still works. Occasionally weird things like "invalid frame header" errors crop up-which is why it is important to keep things simple.

Did you find this page helpful?