Introduction and use case
Hey friends, welcome. In this one we're going to learn how we can use WebSockets with SvelteKit. This is a tutorial on using WebSockets and how they work, but how to get it working in SvelteKit so you can work on your project, like if you have a multiplayer game, etc. In my use case I was working on an editor for Jerk Code and here on the right I'm using WebSockets to have a live preview because what you see on the left gets sent to the server and it gets crunched in some markdown processing chain and then I get an output on the right, so I get a live update preview.
Problem and initial research
So that's my use case, but I ran into a problem: I couldn't figure out how to get WebSockets working. So I started digging around and I figured it out, so let me share that with you. Here I'm only using a normal SvelteKit project, nothing special. I'm running the development server and on the side is the project. In the post I mentioned this might change in the future and SvelteKit might have support for WebSockets natively, so it might be something like this if you're watching this in the future, but this doesn't exist yet unfortunately. You can read more about this in the discussion here that I am going to link in the post. If you go there you can see all this interesting discussion. Also thanks to Bob Fanger for giving me this helpful tip here how to make this work in development. Another solution is to create a separate server but then you have to run two things on separate ports. Let me show you how we can use SvelteKit instead. First we're going to explore how to do it in development, which is simple: we're going to write a simple Vite plugin and hook into its development server. It sounds complicated, but it's really not.
Vite plugin for development WebSockets
If you can see here this is the example called — this is the entire plugin, almost, right? There's more Socket.IO code than Vite to be honest with you. So the only other thing you're going to need, because this is a tutorial on WebSockets, I'm using Socket.IO as an example which is a really awesome library, so you don't have to worry about WebSockets, and you're going to need the Svelte adapter node because it doesn't work without the Svelte adapter node and you're going to learn why. So we can close this. The first thing to make this work in development is go to the Svelte config. As I show in this code you have to change this from adapter auto to node. Then we can import server from Socket.IO and then we're going to create our plugin. So we can say export const websocketServer which is an object. Let's give it a name: socketServer. Then let's use configureServer, it gives us a server, and we can say const io = new Server. We're going to use the server and we're going to get the http server from it. If you're not sure where this is from, this is from Vite and you can learn that from the documentation. So yeah, so we can use our WebSockets. We can say on connection, so on connects, say socket, and then we can say socket.emit 'event-from-server' because we're going to catch it on the client. You can say 'hello world'. Let me just copy over this emoji. Oops. Yeah, and that's almost it, so we just need to use the plugin. Here we can seek it — let me just close the terminal because, yeah. So we can save it. You should get auto completion. Plugins, and then we can say websocketServer, and then we can save it. But don't remember: you have to restart your server. But first let me just include the client code, and also if this gets out of hand you can just abstract this into a separate file, and that's it.
Client-side Socket.IO setup and testing
So let me just go to src/routes. Here is our index file. I just want to show you how it would look like on the client. So we can go here and we can say and then we can import Socket.IO on the client. We can say io from 'socket.io-client'. const socket = io(); Let's invoke it and I'm going to say 'event-from-server'. Again, this isn't a WebSockets tutorial, but I just want to give you some helpful information and then we can log this out. So if you're unsure with WebSockets, here is where we emitted this event from and our client should catch this. One thing we have to do is restart our development server, so let me go here and you know me, I'm always paranoid. Let me just start this up in 'pnpm dev' and we should see the message here when we restart this, and here it is: 'hello world'. And that's awesome. So now you have WebSockets working in development, but the problem is how do you get it working in production, which is the second step.
Why adapter-node and building for production
Also before you go here let me just show you something awesome: if you have some reactive value and you need to emit it when a value changes you can just use a reactive block in SvelteKit which is really awesome. So you can send the message to the server this way, just letting you know. Let's go to the production. We're going to need Express. Let me see if I already installed it. Yeah, Express, which is awesome. I think I can close this. So let me explain why we need the Node adapter. Let me just stop the development server. Let me run 'pnpm run build'. If you're using npm the commands are the same. So let me just show you the side here so you can see what's going on. I'm going to collapse this. 'pnpm run build' and this should work. Here you can see it created a build folder and here is the index and a handler.js. So we can create our own custom Express server and we can pass it the handler as an Express middleware. So SvelteKit is going to take care of everything else like the routing, etc. So we don't really have to run two servers, right? So that's what I explained here in the post. Let's see how this works in practice.
Custom Express server with Socket.IO
At the root of the project, just because it's easier to include it in the script in a moment, I've created a folder 'server'. So let's create 'server/index.js'. Let me just collapse everything. Again, we're in the root server/index.js but you can place it whatever you want or whatever your build step is. Let me just close the terminal and let's just make our own little custom server so you can see. import express from 'express' and don't worry if you don't understand back-end; I'm not the backend expert, I just made this work and I'm showing it to you now. Create server from 'http' — this is our server that we previously passed from Vite. Then we're basically doing the same thing, so we're saying io = new Server from socket.io and here is the handler. So we need to save from then we need to go to the build, we need to get the handler, and then we define port 3000. Then we say 'const app = express()' then we create the server, createServer(app), pass the app. Again same as before we say io = new Server(server) and then we can say io.on('connection') and then again we have the socket and we say socket.emit 'event-from-server' you can say 'hello world'. Let me copy our emoji. I actually have an emoji picker but if I would use it here it would mess with the recording, so that's why I always copy the emojis if you're curious. Here I even commented for you how this works and I link to the GitHub repository where this code is. So yeah, we can use the middleware. app.use and middleware in Express is basically it intercepts the response and request object and it changes it. Basically SvelteKit is here like, Jesus, he takes the wheel and we don't have to worry about anything else. Then we say server.listen(port) and then we save this.
Start script, recap, and final test
For the last step go to your package.json. We're going to add a script. If we scroll down here you can see our scripts. We can include a start script and say 'node server/index.js'. Yes, we're just invoking it with node. This is going to start the SvelteKit production server and our WebSocket. So we can save this. Let me just recap: we created a custom server by creating a custom Express server that we're going to create a script here and start it. So this starts instead of the one in the build because usually SvelteKit would run this. Here it describes the same thing and it shows you here how it works. Let me just close this and I'm going to close the sidebar. For the moment of truth don't forget we have to run our server. So you don't run 'npm run dev' but now we have to execute our script which if you remember is 'start' and we can just do that by saying 'pnpm start' — you don't have to say 'run'. So everything should work. We're now in production and if I refresh this it works. Also if you need to pass environment variables I have a post on using environment variables in SvelteKit. So that's it. Thanks for watching and catch you later.