Payload case conversion in Phoenix WebSockets

Victor Viruete
Stuart Tech
Published in
3 min readJun 12, 2018

Lately I’ve been working on a personal side project which uses an Elm single page application on the front end, and a Phoenix application on the back end. In our setup there isn’t any RESTful API, all the data flows through websockets.

Dealing with naming conventions

As you may know, the naming convention for variables in Elm and Elixir are different. The former follows camelCase while the latter follows snake_case. We wanted to be respectful of the conventions in each of the languages but we needed to transform the payloads at some point. The decision was to keep the frontend simple so we delegated this job to Phoenix websockets.

The naive solution was to manually transform each of the payloads from snake case to camel case, or the other way around depending on the direction of the message. However this was going to be tedious and painful to manage in the long term. So we preferred a healthier approach based on application level configuration.

Finding the answer

The question is how? If you’re building a regular HTTP API the solution is quite straight forward. You can use proper_case or accent packages. Both of them make it really easy by supplying Plugs that you can take adventage of. However websockets are a different case. They don’t use plugs and Phoenix’s format encoder configuration hasn’t any effect on channels payloads. So what’s the best way to achieve it then?

Trying to do some research I came across this issue open by Michał Muskała where he asks how to change the JSON encoder for websockets. Ok, it isn’t the very same requirement we have but it’s close. We both need to alter the payloads at some point so a solution for him may work for us as well.

Fortunately Phoenix Framework’s very own creator Chris McCord explained how to make that possible. It basically needs a custom websocket serializer. In Phoenix it has to be done twice as it implements two different versions of the websocket protocol (1.0.0 and 2.0.0).

Before 2.0 it seems very unlikely that we’ll have a simpler solution but, for the time being, writing 2 files of around 20 lines each doesn’t seem a huge pain.

Solution implementation

As I mentioned, the solution is not complex at all. It just involves adding a couple of files and changing the UserSocket module to use our custom serializers. I’ve put together a working example on this GitHub repository however below you can find each step explanation.

Case conversion

At the beginning of the post I talked about the proper_case package. Although we are not going to use its plug given we work with websockets, it’s still a handy tool we can benefit from to change the case of our payloads keys.

So the first step would be adding it to our Mixfile.

Now it’s time to write our custom serializers.

Writing our custom serializers

The next step is the most important one, writing our own custom websocket serializers. This is not the stuff you do on a daily basis within a Phoenix project, so I wasn’t sure what was the best folder to put them. Inside channels/socket_serializer folder is probably where they fit best.

There we’ll create v1.ex and v2.ex modules which are basically a copy of Phoenix’s WebSocketSerializer and its second version respectively. Why are there a couple of implementations of the protocol? I’m not really sure yet 😅

Now that we have our serializers based on the original Phoenix ones it’s time to change them. The serializers include functions for encoding and decoding the messages. We just need to change them to transform the payloads the way we want. In our case, given we have an Elm frontend and an Elixir backend, we need to encode payloads with pascal case and decode them with snake case.

The same applies for v1 file but it’s slightly different. You can check it out here.

Wiring all up in the socket

Now that we have our custom serializers in place we can use them in our socket module.

And that’s all. Following these few steps you would be able to be consistent with naming conventions on each side of your application.

--

--

Victor Viruete
Stuart Tech

Web developer who from time to time writes about Elixir, Javascript, Elm and other stuff