Reputation: 835
I'm trying to create a simple WebSocket server that will run in a SvelteKit application. I found this tutorial online which shows how to do it using Socket.io, however I would like to use the ws
module instead.
This is the vite.config.ts
file that I've come up with so far:
import type { UserConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite';
import { WebSocketServer } from "ws";
const webSocketServer = {
name: "webSocketServer",
configureServer: () => {
const webSocketServer = new WebSocketServer({
port: 8080
});
webSocketServer.on("connection", (socket, request) => {
socket.on("message", (data, isBinary) => {
console.log(`Recieved ${data}`);
});
socket.send("test from server");
});
}
}
const config: UserConfig = {
plugins: [sveltekit(), webSocketServer]
};
export default config;
I can connect to this WebSocket server from the frontend (in +page.svelte
, for example) and send messages between them.
But every time I make a change to my server's code and save the file, I get an error saying that "there's already something listening on port 8080" and my Vite dev server terminates. If I then start back up my Vite dev server by running npm run dev
then it will all work fine again, until I make any change to the WebSocket server code and save the file, and then the same thing will repeat.
I would rather not have to re-start my Vite dev server every time I make a change to my WebSocket server's code. If possible, I would rather Vite automatically shut down my old webSocket server to make room for the new one before booting it up each time so that they won't conflict.
The tutorial I linked above shows this code snippet:
import adapter from '@sveltejs/adapter-node'
import preprocess from 'svelte-preprocess'
import { Server } from 'socket.io'
const webSocketServer = {
name: 'webSocketServer',
configureServer(server) {
const io = new Server(server.httpServer)
io.on('connection', (socket) => {
socket.emit('eventFromServer', 'Hello, World 👋')
})
},
}
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: preprocess(),
kit: {
adapter: adapter(),
vite: {
plugins: [webSocketServer],
},
},
}
export default config
This snippet is using an old SvelteKit version where Vite configuration was done inside the svelte.config.js
file, so the layout is a little different, but it seems they're simply spinning up their Socket.io server directly inside the configureServer
method just like I am, but they're tapping into the existing Vite http server instead of creating a new one like I am. I tried doing this and I still get the same problem. Every time I try httpServer.listen(8080);
on Vite's http server I'm told that the server was already listening on a port and the dev server terminates. I also tried manually creating an http server using require("http").createServer()
and using that, but (unsurprisingly) that also did not work and acted identically to my initial attempt shown at the beginning of this question.
The tutorial seems to be booting up a Socket.io server the same way I'm trying to boot up mine, but they don't seem to be running into any conflicts like I am. I checked Socket.io's documentation to see if perhaps the Server
constructor has a built-in failsafe to make sure it doesn't listen on a port if it's already listening on that port (and avoid creating the error), but the docs didn't give any information in that regard, so I'm still unsure as to what's going on there.
Is there any way to do what I'm trying to do, or am I going about this entirely the wrong way? I can't find hardly any information about this anywhere on the internet.
And also, what's going on in the Socket.io example that allows it to work where mine won't? Is Socket.io doing something special?
Here's my attempt at using Vite's built-in http server. This behaves the same as my other attempts.
vite.config.ts
import type { UserConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite';
import { WebSocketServer } from "ws";
const webSocketServer = {
name: "webSocketServer",
configureServer: (server: any) => {
const httpServer = server.httpServer;
const webSocketServer = new WebSocketServer({
noServer: true
});
webSocketServer.on("connection", (socket, request) => {
socket.on("message", (data, isBinary) => {
console.log(`Recieved ${data}`);
});
socket.send("hi c:");
});
httpServer.on("upgrade", (request: any, socket: any, head: any) => {
webSocketServer.handleUpgrade(request, socket, head, socket => {
webSocketServer.emit("connection", socket, request);
});
});
httpServer.listen(8080);
}
}
const config: UserConfig = {
plugins: [sveltekit(), webSocketServer]
};
export default config;
Upvotes: 4
Views: 10368
Reputation: 74871
ws supports initialising with an existing server
like socket.io
Don't store the application logic in the vite/svelte config, as it will be needed for the production build when vite is not available (I'm not familiar with sveltekit though so maybe it does some magic?). Create a standalone file for the ws setup:
import { WebSocketServer } from "ws";
export const configureServer = (server) => {
const webSocketServer = new WebSocketServer({
server: server.httpServer,
});
webSocketServer.on("connection", (socket, request) => {
socket.on("message", (data, isBinary) => {
console.log(`Recieved ${data}`);
});
socket.send("test from server");
});
}
export const webSocketServer = {
name: "webSocketServer",
configureServer,
}
Then you should be able to use your version of webSocketServer
like the socket.io example as they both attach to the '@sveltejs/adapter-node' server.
import adapter from '@sveltejs/adapter-node'
import { webSocketServer } from './sockets.js';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter(),
vite: {
plugins: [webSocketServer],
},
},
}
export default config
configureServer
can now be reused when you setup the custom server for production.
Upvotes: 5