SvelteKit with SocketIO in Production

sveltekit-with-socketio-and-nodejs-285h

Read if:

  • You wanna learn how to use SocketIO with SvelteKit.

Resources

Introduction

This is going to be a series, this is first part of the series and from now on in every 2 days I'll release a new part of this series. Where I'm gonna make a fully fledge chat app using SvelteKit, socketIO, Express and maybe redis. This chat app going to have some features like: Making a group for chat, single chat and allow to join private group link using invitation.

Here I'm going to explain hoe to integrate SocketIO in SvelteKit because SvelteKit doesn't have a built-in support for WebSocket and we have to find a way to use SocketIO in the project for development and production.

Setup the project

First setup your SvelteKit project using init.

npm init svelte@latest anonChatApp
cd anonChatApp
npm install
npm run dev -- --open
Enter fullscreen mode Exit fullscreen mode

I'm using Tailwind CSS in this demo. I've already written an article on Using TailwindCSS in SvelteKit to make a Design System : Part One.

Here we will gonna use node adapter because of SvelteKit doesn't have built-in support for WebSockets. So we can't use auto adapter which help us to deploy it on vercel which is now out of the scope, we have to deploy it on our sever.

  • We are going to install adapter-node in our project
npm i -D @sveltejs/adapter-node
Enter fullscreen mode Exit fullscreen mode
  • Make the adapter changes in svelte.config.js file
import adapter from '@sveltejs/adapter-node';
import preprocess from 'svelte-preprocess';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    // Consult https://github.com/sveltejs/svelte-preprocess
    // for more information about preprocessors
    preprocess: [
        preprocess({
            postcss: true
        })
    ],
    kit: {
        adapter: adapter()
    }
};

export default config;

Enter fullscreen mode Exit fullscreen mode

Adding SocketIO server

Install the dependencies for socket.io for server, socket.io-client for client and we also going to need express.

npm i socket.io socket.io-client express
Enter fullscreen mode Exit fullscreen mode

Here We going to make a vite plugin that going to help us with WebSocket in development version.

// webSocketPluginVite.js 
import injectSocketIO from './socketIoHandler.js';

export const webSocketServer = {
    name: 'webSocketServer',
    configureServer(server) {
        injectSocketIO(server.httpServer);
    }
};
Enter fullscreen mode Exit fullscreen mode
  • Above we are importing injectSocketIO from socketIoHandler.js. This file handles are all socketIO events e.g. connection, message, error.
// socketIoHandler.js
import { Server } from 'socket.io';

export default function injectSocketIO(server) {
    const io = new Server(server);

    io.on('connection', (socket) => {
        let username = `User ${Math.round(Math.random() * 999999)}`;
        socket.emit('name', username);

        socket.on('message', (message) => {
            io.emit('message', {
                from: username,
                message: message,
                time: new Date().toLocaleString()
            });
        });
    });

    console.log('SocketIO injected');
}

Enter fullscreen mode Exit fullscreen mode

That's a normal setup for dev now we have add plugin to our vite.config.js. So we can use websocket in development.

import { sveltekit } from '@sveltejs/kit/vite';
import { webSocketServer } from './webSocket.js';

/** @type {import('vite').UserConfig} */
const config = {
    server: {
        port: 3000
    },
    preview: {
        port: 3000
    },
    plugins: [sveltekit(), webSocketServer]
};

export default config;
Enter fullscreen mode Exit fullscreen mode
  • Above i have imported our plugin and added to vite plugin configuration which will now run our WebSockets in development mode.

  • But we have to add more to make it work in production. So, we have to make a build using node adapter that we configured in the start.

npm run build
Enter fullscreen mode Exit fullscreen mode
  • This will generate a build for your project now we have to extend our SvelteKit server to use WebSockets in it. That's why we needed express which is going to help us extend our default server of SvelteKit.
// server.js

import http from 'http';
import express from 'express';
import injectSocketIO from '../socketIoHandler.js';
import { handler } from '../build/handler.js';

const app = express();
const server = http.createServer(app);

// Inject SocketIO
injectSocketIO(server);

// SvelteKit handlers
app.use(handler);

server.listen(3000, () => {
    console.log('Running on http://localhost:3000');
});
Enter fullscreen mode Exit fullscreen mode
  • Now you can make a build and run it on production which is running on port 3000.

Adding SocketIO on Client

We already installed the dependency for client side of SocketIO. We gonna add a file in lib folder webSocketConnection.js which going to handle our socket connection for client.

import ioClient from 'socket.io-client';
const ENDPOINT = 'http://localhost:3000';

const socket = ioClient(ENDPOINT);

export const io = socket;
Enter fullscreen mode Exit fullscreen mode
  • In this we are exporting our socket which uses our endpoint which is running in port 3000.

  • Now we gonna take a look on client uses of SocketIO

<script lang="ts">
    import { io } from '$lib/webSocketConnection.js';
    import { onMount } from 'svelte';

    let textfield = '';
    let username = '';

    let messages = [];

    onMount(() => {
        io.on('message', (message) => {
            messages = [...messages, message];
        });
        io.on('name', (name) => {
            username = name;
        });
    });

    function sendMessage() {
        const message = textfield.trim();
        if (!message) return;

        textfield = '';
        io.emit('message', message);
    }
</script>

<div class="h-screen w-screen bg-zinc-800">
    <div class="h-full w-full max-w-md mx-auto bg-zinc-500 flex flex-col">
        <header
            class="px-6 py-4 border-b border-zinc-800 bg-zinc-700 text-white shrink-0 flex items-center justify-between"
        >
            <span class="font-bold text-xl">My Chat app</span>
            <span>{username}</span>
        </header>

        <div class="h-full w-full p-4">
            {#each messages as message}
                <div class="bg-zinc-300 rounded-xl rounded-tl-none px-4 py-3 my-4 w-fit">
                    <span class="flex items-center space-between gap-4">
                        <b>{message.from}</b>
                        <i>{message.time}</i>
                    </span>
                    {message.message}
                </div>
            {/each}
        </div>

        <form
            action="#"
            on:submit|preventDefault={sendMessage}
            class="px-6 py-4 border-t border-zinc-800 bg-zinc-700 text-white shrink-0 flex items-center"
        >
            <input
                type="text"
                bind:value={textfield}
                placeholder="Type something..."
                class="bg-transparent border-none px-4 py-3 w-full"
            />
            <button type="submit" class="shrink-0 border border-white rounded-lg px-4 py-3">Send</button>
        </form>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode
  • As above in script section we are importing are client side socket connection and going to use it in our onMount function so we can get all the new messages and a username that is randomly generated on server. For sending message we are using emit in sendMessage function to send message on server and broadcast to all users.

It a basic setup for SocketIO on dev and prod in SvelteKit. We will continue in second part from here. If you wanna suggest some add on functionalities or features for project please comment it down.

Future me writing this, due to SvelteKit going to make some breaking changes in next update.

This is me writing for you. If you wanna ask or suggest anything please put it in comment.