suhaildawood / SvelteKit-integrated-WebSocket

First-class support for WebSockets within SvelteKit by attaching a WebSocket server to the global state.
MIT License
219 stars 13 forks source link
svelte sveltekit websocket

SvelteKit with Integrated WebSocket Server

Updated: July 4, 2023, compatible with SvelteKit 1.21.0 and Svelte 4.0.4

First-class support for WebSockets within SvelteKit by attaching a WebSocket server to the global state.

hero_image

Developing

Once you've created a project and installed dependencies with pnpm install, start a development server on localhost:3004:

pnpm run dev --open

Previewing

Preview the production build on localhost:3003:

pnpm run preview --open

Building

To create a production version of the app and start a server on localhost:3005:

pnpm run build
pnpm run prodServer

Write-up

Svelte has taken the web development community by storm. It provides a foundation for highly performant web applications, and in practice I've found it to be of even more value when developing larger, more complex projects. Powered with best-in-class front-end tooling powered by Vite, SvelteKit is a flexible, multi-platform approach to building websites and web applications. SvelteKit recently hit 1.0 in December 2022, making it the ideal framework for any new projects, whether they be small pre-rendered static sites or large, multi-faceted client-heavy applications.

One glaring omission from the stable release of SvelteKit has been out-of-the-box integration for WebSockets. In many modern web applications, WebSockets are the preferred channel for real-time communication. With first-class support, the advantages of co-locating server-side code within the SvelteKit project structure is extended to real-time application logic. Utilities, logic and types can be shared across the codebase.

As of SvelteKit 1.21.0 (July 4, 2023), WebSockets are not supported out-of-the-box. The recommended way of integrating a custom WebSocket server within SvelteKit is through middleware. The helpful SvelteKit community has therefore provided a temporary solution:

All of this works but is not particularly elegant and comes with some significant drawbacks:

This isn't ideal and it turns out that there's a better way to integrate a WebSocket server within SvelteKit in a manner that doesn't require a separate build step and makes the WebSocket server context available in all server-side SvelteKit code.

Rather than build the WebSocket server and/or logic as an isolated component, we can define a set of utility functions to:

  1. Create a new WebSocket server and attach it to a global instance variable.
  2. Define a function that upgrade certain HTTP requests to WebSocket connections.

These utilities will be used in development and production and reside within the SvelteKit project structure: $lib/server/webSocketUtils.ts.

For development, we'll still define an Vite plugin, but rather than import and attach a compiled middleware file, we'll import the two functions from webSocketUtils to set up a new WebSocket server and handle new connections.

For production, we'll set up a new file at the top-level directory called prodServer.ts. This script:

  1. Imports and runs the built version of our SvelteKit application (via pnpm run build)
  2. imports the same two functions from webSocketUtils and runs the WebSocket server alongside the SvelteKit server

In both development and production, the trick is to attach the WebSocket server to globalThis, representing the global object. This WebSocket server instance is attached to the global state via a custom JavaScript Symbol via Symbol.for(). This guarantees predictable, runtime-wide access to the server instance.

What this allows for is full control of the WebSocket server in our SvelteKit server-side logic. In hooks.server.ts, we can call a setup function that extends the WebSocket server and includes any logic that would need to run when either the development or production server is established. In addition, we can define custom logic in the handler function to attach the WebSocket server context to event.locals. With this, you could:

With this, we achieve first-class support for WebSockets within SvelteKit!

References

Thanks to the following resources: