kit icon indicating copy to clipboard operation
kit copied to clipboard

Native support for web sockets

Open AndreasHald opened this issue 4 years ago • 81 comments

We've been attempting to migrate our app from sapper to sveltekit and ran into the problem of web sockets, in sapper we could simply attach the WebSocket server directly to the express server and be done with it, but as per Rich Harris' comments here and here here in sveltekit we don't want to expose direct access to the middlwares thus promoting the node adapter over the serverless ones.

However seeing as some of the serverless providers are starting to support web sockets (cloudflare) (begin) (AWS Lambda) should sveltekit provide a way to handle these connections directly?

A native approach to this could also potentially provide a tool to solve this issue and this

Describe the solution you'd like An interesting solution could be to expose functions in endpoints in the same way we handle http requests, and then let the adapters handle the rest

routes/api/index.ts

// Handle post requests
export const post: RequestHandler = async (request) => {....}
// Handle web socket requests
export const ws: WebSocketRequestHandler = async (request) => {....}

A concern with this approach could be mixing protocols, within an endpoint could be confusing. Another could be if this becomes too complicated since as far as I know the initial handshake to start a WebSocket connection is handled over http, would this then be handled directly by sveltekit which could be considered too much "magic" since it would have to intercept a get request that would then not make it to the get handler in the endpoint. Or should we let the user implement the get endpoint and return the WebSocket pair and upgrade header.

Describe alternatives you've considered The way we go about it today is a small script that manipulates the build output of sveltekit as such however this does not work in dev mode which is a pain point.

const instance = createServer({ render: app.render }).listen(PORT, HOST, (err) => {
	if (err) {
		console.log('error', err);
	} else {
		console.log(`Listening on port ${PORT}`);
	}
});
app.server.installSubscriptionHandlers(instance);
export { instance };

Is this a feature that you feel would suit sveltekit? are there any considerations I've overlooked?

AndreasHald avatar May 19 '21 09:05 AndreasHald

@AndreasHald Have you ever heard of Mercure?

Gregcop1 avatar May 28 '21 21:05 Gregcop1

@AndreasHald Have you ever heard of Mercure?

Can't say i had. The idea looks interesting, but afaik sveltekit doesn't yet support SSE yet either.

We are pretty invested in Apollo Graphql server / client, and i don't think they have a transport layer for mercury, but correct me if i'm wrong.

AndreasHald avatar May 29 '21 16:05 AndreasHald

Hi! Mercure creator here.

sveltekit doesn't yet support SSE yet either

Basically, as long as you delegate to a Mercure hub (a server implementing the protocol), you don't have to deal with SSE directly. The app just have to do a POST request to the hub to broadcast an update to all connected clients.

Supporting this part of the protocol perfectly fits with serverless environments because you can delegate the long running connection (that is by nature incompatible with serverless) to a dedicated component, even if the serverless service itself is not compatible with SSE, Websockets and the like.

We are pretty invested in Apollo Graphql server / client, and i don't think they have a transport layer for mercury

I don't know Apollo very well. I know that API Platform has native support for GraphQL subscriptions through Mercure, and AFAIU there is no need for client-side code. Using the native EventSource JS class looks enough. But maybe that a better integration with Apollo could be created?

Anyway, I would love to see support for the protocol land in SvelteKit. Let me know if I can help in any way to make this happen (I can try to create a transport for Apollo for instance).

dunglas avatar Jun 03 '21 13:06 dunglas

I had the same requirement and initially got some dirty workarounds to make it work with just SvelteKit but it's not maintainable. I have come to understand the direction SvelteKit is being steered towards(i.e. serverless rendering) that makes it quite tough to be fully compatible with the traditional NodeJS SSR + Vite itself would still lead us to a lot of bundling issues for server-side libraries. After playing with SvelteKit for a few weeks, I decided to build a set of toolkit on top of SvelteKit and bring out the best possible monolith DX. For more details, please find my comment here.

Though I haven't added:

  • export const ws to support websocket
  • export const graphql to support GraphQL
  • export const grpc to support GRPC (haven't dive deep into this yet but will be very much needed for successful startups who wants to slowly move some monolith pieces out to another service)

but yes, the above are all planned! I'm just excited to see this post here as I was wondering the same why we can't support export const ws and export const graphql! Good job.

cayter avatar Jul 10 '21 05:07 cayter

@cayter sounds interesting, i think we will migrate to use SSE when 1568 is resolved, as i understand there is a PR ready.

How would you go about implementing export const graphql.

Svelte kit supports the graphql standard very well out og the box. Because graphql specifies a single endpont just use a single file /graphql/index.js that supports the spec

We use apollo server without any issues.

I had the same requirement and initially got some dirty workarounds to make it work with just SvelteKit but it's not maintainable. I have come to understand the direction SvelteKit is being steered towards(i.e. serverless rendering) that makes it quite tough to be fully compatible with the traditional NodeJS SSR + Vite itself would still lead us to a lot of bundling issues for server-side libraries. After playing with SvelteKit for a few weeks, I decided to build a set of toolkit on top of SvelteKit and bring out the best possible monolith DX. For more details, please find my comment here.

Though I haven't added:

  • export const ws to support websocket

  • export const graphql to support GraphQL

  • export const grpc to support GRPC (haven't dive deep into this yet but will be very much needed for successful startups who wants to slowly move some monolith pieces out to another service)

but yes, the above are all planned! I'm just excited to see this post here as I was wondering the same why we can't support export const ws and export const graphql! Good job.

AndreasHald avatar Jul 10 '21 11:07 AndreasHald

Svelte kit supports the graphql standard very well out og the box. Because graphql specifies a single endpont just use a single file /graphql/index.js that supports the spec We use apollo server without any issues.

I probably need to give it a try again, I bumped into issues where Vite was bundling some of the server-side dependencies in the client bundle and broke the page rendering. But yeah I was thinking of supporting graphql (with websocket subscription) easily via export const graphql though I haven't really come to this step yet, will share more once I'm onto it and will very likely be adopting:

  • https://github.com/websockets/ws
  • https://github.com/enisdenjo/graphql-ws

cayter avatar Jul 10 '21 11:07 cayter

I think we will be exposing a way to add middleware in adapter-node (https://github.com/sveltejs/kit/pull/2051), but if anyone wants to take a stab at adding more generic support that also support the serverless platforms, that still sounds valuable

benmccann avatar Aug 04 '21 22:08 benmccann

So is there some guideline regarding how to integrate sockets with a svelte-kit app? As far as I understand, the path of least resistance at the moment is to use adapter-node, and add a file that uses the built svelte site:

import { assetsMiddleware, prerenderedMiddleware, kitMiddleware } from './build/middlewares.js';
import polka from 'polka';
import Socket from 'socket.io';

const { PORT=3000 } = process.env;

const app = polka();

app.use(assetsMiddleware, prerenderedMiddleware, kitMiddleware);

const { server } = polka().listen(PORT, () => console.log(`> Running on localhost:${PORT}`))

const io = Socket(server);
io.on('connection', socket => {
  console.log('connection')
})

I didn't try this yet, but it seems to be the recommended path. It bothers me somewhat because it isn't integrated in the build system (won't have access to the same environment variables and import.meta goodies, won't be using typescript unless I set up a separate build, ...).

But assuming I go with that, but I am not sure how access to all the session information that I have in my svelte-kit app. I am also not very clear on changing the state of the socket server based on url calls. Should I send two requests from each URL in my svelte-app (one normal http call and one to the websocket server?) Should I add a middleware to adapter-node?

Xananax avatar Sep 08 '21 19:09 Xananax

I didn't try this yet, but it seems to be the recommended path. It bothers me somewhat because it isn't integrated in the build system

@Xananax, well I did. And actually it is easy to integrate with the build process. You can specify your custom server as entryPoint in your svelte.config.js file. Your can read about that here: https://github.com/sveltejs/kit/tree/master/packages/adapter-node#entryPoint

An example:

  • Put your custom server at your [YOUR_PROJECT_DIR]/server/index.js right next to [YOUR_PROJECT_DIR]/src
  • Do not forget to add polka to your dev dependencies and to run npm installagain
  • Add entryPoint: 'entryPoint: 'server/index.js',' in your adapter option within the kit.adapter section of your svelte.config.js
  • run npm build and see the magic happen.

matths avatar Sep 24 '21 09:09 matths

I don't think that we'd want to provide built-in endpoints in that manner. I would really like to provide the feedback to your teacher that this assignment is not fair to open source maintainers. I welcome people getting involved in open source, but would encourage them to do so because of their own experience with the project. I've had this happen numerous times where people need to check a box because of a school assignment and it almost never results in code that we can accept to the project and instead just places more burden on us. This isn't your fault at all and I'm happy you're interested in Svelte so I hate to write something discouraging. But I do wish this trend of making us do more work for other people's school assignments would die. Your teacher should be responsible for reviewing all code changes and not making us do it. I am simply not interested in being a TA and will reject any contributions to the project without review if they are for a school assignment because it is not a scalable use of our time to do teaching and review of school assignments. That's a teacher's job and they should stop trying to foist their job upon others who didn't volunteer for such duties

benmccann avatar Sep 24 '21 18:09 benmccann

Any news on Websockets, is there an ETA when Websocket will be easily available within SvelteKit for DEV and PROD?

Looking at the Endpoints docs with its await db.get(slug) I think SvelteKit is clearly seen as backend, so supporting Websockets seems just the next logical step.

bluepuma77 avatar Oct 26 '21 22:10 bluepuma77

Workaround

Inject socket.io (or another websockets implementation) into the vite DEV server via the vite config:

import { Server } from "socket.io";
export default {
 plugins: [
    sveltekit(),
    {
      name: "multiplayer",
      configureServer(server) {
          const io = new Server(server.httpServer));
          // do websocket stuff
      },
    },
  ]
}

https://github.com/bfanger/multiplayer-dice-game/blob/main/vite.config.ts

For PROD use the node-adapter to generate the middleware and which you'll include in a custom server: https://github.com/bfanger/multiplayer-dice-game/blob/main/server.js

bfanger avatar Oct 30 '21 12:10 bfanger

Using adapter-node as middleware along with a websocket server outside of sveltekit means that, if for example you are using TypeScript and .ts database models (i.e.: my current structure is lib/db/models/[...].ts, fetched from endpoints), you'll need a lot of extra steps to make these usable. In my case this makes it not worth it to even support real time updates :/

glics avatar Nov 09 '21 11:11 glics

@Rich-Harris any guidance on the priority to support websockets in ‘22? We have a realtime api based on Elixir and Phoenix, and the story around websockets is the only aspect giving us pause with adopting sveltekit.

sjmueller avatar Jan 29 '22 07:01 sjmueller

It bothers me somewhat because it isn't integrated in the build system (won't have access to the same environment variables and import.meta goodies, won't be using typescript unless I set up a separate build, ...).

This should now be addressed since https://github.com/sveltejs/kit/pull/2931. Previously, adapter-node had a second bundling step using esbuild. Now Vite does everything

benmccann avatar Jan 29 '22 19:01 benmccann

@benmccann It would be great if we could get a quick description (or link to it) how we can use WebSocket with SvelteKit.

Specifically for those web developers not so much involved with the inner workings of SvelteKit and Vite.

bluepuma77 avatar Feb 17 '22 21:02 bluepuma77

+1 for WebSockets. I love SvelteKit and wanted to build a real-time game on it, but lack of support requires me to separate my back end and front end at this point.

kubikdanon avatar Apr 04 '22 16:04 kubikdanon

As of now, Cloudflare Workers, Deno Deploy and AWS are supporting WebSockets There is gonna be more and more WebSocket support on serverless platforms, having some native and first-class WebSocket support is something we truly need to make immersive experiences that can go realtime

superboss224 avatar May 28 '22 02:05 superboss224

Any updates on this?

kevin192291 avatar Jul 05 '22 05:07 kevin192291

This really is the last piece that's missing in SK to finally replace all my other stacks. Hope it will be there soon :-)

VirgileD avatar Jul 19 '22 12:07 VirgileD

Just FYI, for those who are not running on serverless technology, you can rely on this to setup your own HTTP server alongside the websocket server. I had successfully setup a Fastify server + tRPC with subscriptions.

cayter avatar Jul 19 '22 13:07 cayter

I am running my own setup (not serverless) I would finally decided to run a node instance as my web-socket server. It is all well and good, but it would be really nice to have this built-in, as it would greatly reduce the maintenance and code complexity. This is currently my ONLY sveltekit ask!!!

kevin192291 avatar Jul 19 '22 13:07 kevin192291

Cloud Run supports Websockets as well, which is a serverless technology... this could change the game in frameworks. I predict this will be a thing one day, just hopefully sveltekit before nextjs. This is so important for modern app development and would make sveltekit the most powerful framework.

J

jdgamble555 avatar Oct 29 '22 00:10 jdgamble555

As scary as it sounds, I think that this may be a make or break feature for SvelteKit VS Nextjs

CropWatchDevelopment avatar Oct 30 '22 09:10 CropWatchDevelopment

It seems solid-start included the initial implementation for websocket, probably SvelteKit can borrow some ideas from it too.

Disclaimer: I'm not sure if it only plays well with node adapter or the serverless platform like netlify/cloudflare worker as well.

cayter avatar Nov 12 '22 05:11 cayter

Hopefully we'll have native websockets very soon but anyone looking for a solution in the meantime should check out JoyOfCode's video or article on a workaround.

zachjensz avatar Dec 14 '22 22:12 zachjensz

When will SvelteKit have support for WebSocket?

Eudritch avatar Dec 19 '22 04:12 Eudritch

Are there any news about the websocket support? I need it for my project and when i tried to separate the client and the server it became very messy.

JumpyLionnn avatar Jan 06 '23 12:01 JumpyLionnn

~~Just keep in mind that "native support" is impossible (afaik) for any serverless deployment, such as vercel.~~

~~This is because this type of deployment does not have a permanently running server that could send WS data to it's clients or keep the connection up in the first place. Using the svelte node adapter means opting-out of serverless deployment.~~

Thanks for the correction @jdgamble555. Still, it's important to point out, that the hoster might not support WS, which could still be a dealbreaker in many cases, which is an important piece of information to know about for a dev starting a project.

If this is the case where you are planning to host your project (for me that would be Vercel), maybe you want to create a separate server for WS. For my project, I opted for a mono-repo architecture like this. so I can keep all the serverless benefits:

/app # the sveltekit app, w/ adapter-auto
/server # a simple express server, w/ socket.io
/shared # shared code. I use it for TypeScript types and shared utility methods, etc. With some tsconfig tinkering you can get your project so you can import your shared code like so: `import xy from '$shared/myCode'`, for both the svelte and the server

Then I simply deploy my svelte app to Vercel and my node express server w/ socket.io to some conventional host.

josias-r avatar Jan 10 '23 08:01 josias-r

Just keep in mind that "native support" is impossible (afaik) for any serverless deployment, such as vercel.

It is definitely NOT impossible for serverless technologies to support a version of web sockets:

Even if you were right, which is not entirely the whole picture, Vercel could theoretically create a different deployment option like the /api folder, say /socket, which would deploy differently than the rest of the app. It is already doing this with edge functions, static files, and regular aws lambdas.

The thing is, this would need to start with Vercel -- given that Vercel supports SvelteKit -- then trickle down to SvelteKit -- and other adapters and services would be modified later to support it.

J

jdgamble555 avatar Jan 10 '23 15:01 jdgamble555