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.
Once you've created a project and installed dependencies with pnpm install
, start a development server on localhost:3004
:
pnpm run dev --open
Preview the production build on localhost:3003
:
pnpm run preview --open
To create a production version of the app and start a server on localhost:3005
:
pnpm run build
pnpm run prodServer
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:
$lib/server/websocket
)./dist/websocket-middleware.js
).ViteDevServer
and allows for upgrading HTTP requests to the WebSocket protocol. At this point you would import the compiled JavaScript WebSocket middleware and attach it to the newly established WebSocket server.@sveltejs/adapter-node
outputs, you would define a new Node.js server that integrates the request handler from SvelteKit, establish the WebSocket server and attach the same compiled JavaScript WebSocket middleware from the development server.All of this works but is not particularly elegant and comes with some significant drawbacks:
$lib/server
) that references environment variables designed to work with SvelteKit (for example via $env/dynamic/private
), this isn't compatible when building via esbuild
. For these cases you'll have to rewrite code to use process.env[...]
.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:
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:
pnpm run build
)webSocketUtils
and runs the WebSocket server alongside the SvelteKit serverIn 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:
handler
function.GET
handlers).With this, we achieve first-class support for WebSockets within SvelteKit!
Thanks to the following resources: