How to build server sent events with Phoenix?

Hi alchemists!

Currently we’re using server sent events (SSE/Eventsource) to notify client applications (written in Go) about model changes/updates in a web application. Each client application consumes the stream which is provided by Rails and its “Live::SSE” feature. As our company plans to (slowly) migrate existing Rails Apps to Phoenix, we also want to replace this Rails API with Phoenix.

While I’ve found many examples of using Phoenix channels, I haven’t found useful documentations or examples for building the server sent events functionality. Our Rails application currently uses Redis PUB/SUB to communicate model changes/updates. Every time a model changes, an event is published to Redis. This event is then consumed on the Rails side and “transformed” to server sent events and streamed to the connected client applications.

We’re not bound to Redis as an internal message dispatcher. I think Phoenix offers a better approach with using channels internally. The only thing we want to keep is the use of server sent events. How would an implementation for server sent events look with Phoenix? Can you provide some (links with) examples?

Many thanks.

1 Like

As far as I’ve seen yet there is no built-in way to do SSE in Phoenix, and as the Phoenix channel API covers that use-case and more via websocket/longpolling, most have not seen a need.

However, there is a good use-case for SSE and it still can be done in Phoenix/Plug.

First, you will want to add the mimetype of "text/event-stream" as an acceptable mimetype at whatever router paths you want.

Second, you will need to send data via normal HTTP chunked encoding if I remember right. I know how to do this in Cowboy, and I know Phoenix/Plug does it via longpolling channels, but I’ve not done it in Phoenix/Plug itself so maybe @josevalim can elaborate on how it is done as a quick googling failed me? ^.^

And the above might not even be necessary if Phoenix Channels already have a built-in way to emulate Server-Side Events, I’ve not checked yet. :slight_smile:

Edit: Just as I send this I find a plug way to send chunked data, apparently it is just the chunk/2 call: https://gist.github.com/rbishop/e7b1886d5e75b2f74d8b ^.^

Some kind of one-way channel interface would be a better way if it exists already though. :slight_smile:

2 Likes

We’ve actually considered one-way channels, but we haven’t found a suitable client for Golang (and other programming languages). I’ve not looked at it more deeply, but can Phoenix channels be used with common websocket clients?

yes, it is doable. Check out the source code for the official phoenix.js client library for reference, here’s direct link to the class/object/whatever responsible for establishing and handling websocket connection:

https://github.com/phoenixframework/phoenix/blob/master/priv/static/phoenix.js#L649

Basically you connect to a websocket by URL and listen to onMessage, where you get a JSON payload to deserialize and interpret. For 1-way connection that could be all you need.

1 Like

As @hubertlepicki put it, yes it is, and that is the general preferred way as it uses websockets for speed if possible, but can fall back to longpolling (if you enable it in your socket on elixir), which longpolling is what is used for SSE anyway, so it covers all use-cases, just might be more powerful than what you need, but it is so so simple to use too and I’d highly recommend it, plus bidirectional channels can randomly be very useful. :slight_smile:

I have written a small library for serializing and parsing server sent events. https://hex.pm/packages/server_sent_event

@railsmechanic have you stayed using SSE’s? I would be interested in other peoples reason for using them, over websockets etc. and experiences using them.

Another quick comment.

I am considering using server sent events as a method for streaming data from the client to a server. would be interested if anyone here has ever gone down that path.

SSE, as far as I know, is not intended for that kind of use case, as the client needs to expose a server port where your “real” server can connect to, in order to receive data. For that kind of information exchange I think Websockets are the way to go, because they’re bi-directional out of the box.

I think we may be at crossed wires. The server is not connecting to the client, no need for any ports. The request (sent from client to server) is deliver chunked like a file upload and the body content is serialized as SSE’s (though at this point the fact they are called SSE’s is not great

OK, got it… :grin:

by the way

1 Like

There is also https://github.com/mustafaturan/sse (Server Sent Events for Elixir/Plug)

1 Like