Stetson Websockets

One of the “coolest” things we can do with Stetson in Purerl and a suitable client written in Purescript is use websockets to send typed messages back and forth without too much ceremony.

The handler for this in Cowboy is cowboy_websocket for which there is the equivalent module Stetson.Websocket.

The first thing we need to do is setup the handler by specifying some sort of message type and our state type (in this case, just ‘unit’).

1
2
3
4
5
6
7
8
-- This is a receiving  handler, which receives the message  typr defined above, and holds a state of 'Unit'
eventsWs :: StetsonHandler EventsWsMsg Unit
eventsWs =
  routeHandler
    { init
    , wsInit: wsInit
    , wsHandle: wsHandle
    , wsInfo: wsInfo

init

At this point in time, this handler is still just a plain old Cowboy handler and we need to signal to Cowboy that we’d like it to start invoking the callbacks for Websockets (Also, it kicks this off in another process so messags can safely be sent to it).

1
  -- init runs in a different process to the ws handler, so probably just run the default handler here

wsInit

Once we’ve informed Cowboy that this is to be a Websocket handler, it’ll invoke our wsInit (websocket_init) in the correct process, so this is the time to subscribe to any messages we might want to forward down to the client.

1
2
3
4
5
6
  -- emitter is of type (msg -> Effect Unit), anything passed into that will appear in .info
  wsInit s = do
    -- Get our pid
    self <- self
    -- Subscribe to the bus, and redirect the events into our emitter after wrapping them in our type
    void $ liftEffect $ SimpleBus.subscribe BookLibrary.bus $ BookMsg >>> send self

wsHandle

We then have wsHandle for messages set to us by the client (websocket_handle) given to us as a Frame (binary, text, ping, etc) and we can easily parse json into our model at this point if we receive a text message, or process the binary etc.

1
  -- Receives 'Frame' sent from client (text,ping,binary,etc)

wsInfo

Finally we have info into which messages sent to our process from elsewhere in Erlang will be received so we can proxy them down to our client in the form of the Frame type (binary, text, ping, etc).

1
  -- Receives messages that were sent into 'emitter', typically so they can then be 'Replied' into the websocket

With the use of messages that can easily be serialised/deserialised to/from JSON defined in a shared folder, the client and server can very easily communicate with a stream of back and forth typed messages.