How to connect Turbo Streams to Spring Boot Websocket?

Hi,

I am trying to figure out how to create a turbo stream that connects to a Spring Boot powered Websocket. I know what to render on the Spring Boot backend.

My problem is:
How do I get the html frontend to connect to the websocket with turbo using html or javascrip?

Do you have any pointers? That would be great and would spare me to figure it out using the rails code.

Thanks!

1 Like

Hi,

after looking at the turbo code at https://github.com/hotwired/turbo/blob/aae160bed764ca7322be1e3a9e00366787d96f7f/src/tests/fixtures/stream.html I tried:

    <script type="module">
    import hotwiredTurbo from 'https://cdn.skypack.dev/@hotwired/turbo';
    hotwiredTurbo.connectStreamSource(new EventSource("/sse"));
   </script>

Yet it does not work but only produce “TypeError: null has no properties”.

Hi, after researching in the forums, github, and twitter, I found this Github repo helpful:

Specifically this for the client side, you will notice that it’s pulling the turbo.js library from unpkg instead:

  <script src="https://unpkg.com/@hotwired/turbo@7.0.0-beta.1/dist/turbo.es5-umd.js" ></script>
  <script>
      Turbo.connectStreamSource(new EventSource("/messages"))
  </script>

And on server side (here I used express.js):

app.get("/messages", (request, response) => {
  response.set({
    "Cache-Control": "no-cache",
    "Content-Type": "text/event-stream",
    Connection: "keep-alive",
  });

  response.on("close", () => {
    response.end();
  });

  response.on("error", () => {
    response.end();
  });

  response.flushHeaders();
  response.write("data:\n\n");

  setInterval(() => {
    const data = renderSSEData(renderMessage("praise Jesus!"));
    response.write(data);
  }, 3000);
});

Hi, thanks that points to the right direction. Yet this still is using server side events and not websockets as shown in the sample video. What I’m trying to get working is with websockets so I will have to dig deeper.

i think on the client-side it would be the same, so just pass the websocket connection to the Turbo.connectStreamSource() function. Have you tried this?

// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080');

Turbo.connectStreamSource(socket);
1 Like

@remast FYI, I’m working on a SpringBoot translation of the hotwire demo chat. Only up to the stimulus controller step in the video (@5m46s in) but plan to work through the entire video.

As best I can tell, this is the only additional client-side markup needed:

<turbo-cable-stream-source 
  channel="Turbo::StreamsChannel" 
  signed-stream-name="IloybGtPaTh2WTJoaGRDOVNiMjl0THpFIg==--dca05871f98cd63d8d421602bced659d0cc7da0746268f789fb6a087662e1e12"
  >
</turbo-cable-stream-source>

I don’t know how the signed stream name is generated, but I did notice that it does not change when you refresh the page or restart the rails server.

The response (after the web socket handshake) from the Rail’s web socket server looks like plain JSON:

{ 
  "identifier": 
     "{\"channel\":\"Turbo::StreamsChannel\",\"signed_stream_name\":\"Iloy...1e12\"}",
  "message":
    "\u003cturbo-stream action=.../turbo-stream\u003e"
}

The message attribute is a <turbo-stream-action> XML element encoded as a JSON string.

So far, the only SpringBoot web socket examples I have seen use the Stomp protocol to communicate with the client, but I don’t think that will work with Turbo client—the rails server 101 response to the ws://localhost:3000/cable request includes the header Sec-WebSocket-Protocol: actioncable-v1-json, which matches up with the JSON I see on the network pane.

1 Like

Ah thanks, that points in the right direction. And yes I also did not find a good vanilla websocket sample for Spring Boot and left it aside for the moment. I’ll get back to it when I find the time.

Hi, I got a working example in Go now. I only got it working by connecting using JavaScript and not with markup but better than nothing.

@mbucc here’s a working Spring example: https://github.com/remast/spring-websocket-turbo

Nice! (I like that you worked it out without the need for for turbo-rails.) I’ll try and look more closely later this weekend. Cheers!

The example-app in this branch of this repo has examples for both WebSocket and SSE using node + ejs: GitHub - twelve17/hotwire-turbo-express at webpack

The README has protocol notes that might help other implementations.

1 Like

I’ve made more progress refining the schemes of how to use WebSockets or SSE to communicate changes across connected clients:

I’ve expanded the example app to explain a little of how each scheme is working behind the scenes:

The changes are now in the main branch: GitHub - twelve17/hotwire-turbo-express

Hope that helps!

1 Like