Zurga

Zurga

How to use MessagePack with LiveView instead of JSON

In a quest to optimize the amount of data sent between the server and client I recently decided to try to use MessagePack instead of JSON. The payloads are about 75% of the size in JSON, which is nice. I have not noticed any performance issues related to encoding and decoding.

There is a StackOverflow answer that I used for the basis for this post and @chrismccord helped me with a crucial bit of information. To get LiveUploads to work, a bit of extra code is necessary.

If you spot any improvements or errors, please tell me and I will update the code.

In assets/app.js you import the MessagePack client:

import {Encoder, Decoder} from "@msgpack/msgpack";
const encoder = new Encoder({ ignoreUndefined: true });
const decoder = new Decoder();

....
const liveSocket = new LiveSocket("/live", Socket, {
  // hooks  and params stuff
  encode: (payload, callback) => {
    // We convert binary payloads to a Uint8Array, so MessagePack will send pack it.
    if (payload.payload instanceof ArrayBuffer) {
      payload.payload = new Uint8Array(payload.payload)
    }
    callback(encoder.encode(payload));
  },
  decode: (payload, callback) => callback(decoder.decode(payload)),
})

The serializer looks like this :

defmodule MyAppWeb.MsgpackSerializer do
  @behaviour Phoenix.Socket.Serializer

  alias Phoenix.Socket.Reply
  alias Phoenix.Socket.Message
  alias Phoenix.Socket.Broadcast

  def fastlane!(%Broadcast{} = msg) do
    msg = %Message{topic: msg.topic, event: msg.event, payload: msg.payload}

    {:socket_push, :binary, encode_to_binary(msg)}
  end

  def encode!(%Reply{} = reply) do
    msg = %Message{
      topic: reply.topic,
      event: "phx_reply",
      ref: reply.ref,
      payload: %{status: reply.status, response: reply.payload}
    }

    {:socket_push, :binary, encode_to_binary(msg)}
  end

  def encode!(%Message{} = msg) do
    {:socket_push, :binary, encode_to_binary(msg)}
  end

  defp encode_to_binary(msg) do
    msg |> Map.from_struct() |> Msgpax.pack!()
  end

  def decode!(message, _opts) do
    decoded = Msgpax.unpack!(mesage)
    payload = Map.get(decoded, "payload")

    # LiveUpload sends us binary, but MessagePack is always binary so we cannot use the opcode in the opts.
    if is_binary(payload) and not String.valid?(payload) do
      Map.put(decoded, "payload", {:binary, payload})
    else
      decoded
    end
    |> Phoenix.Socket.Message.from_map!()
  end
end

And then in your Enpoint module use the serializer in the socket:

socket("/live", Phoenix.LiveView.Socket,
  websocket: [
    serializer: [{MyAppWeb.MsgpackSerializer, "2.0.0"}]
  ]
)

Most Liked

Zurga

Zurga

Update, the check in serializer does not work when an array of zeroes is sent. It is probably better to check the “event” key in the message directly.

So the decode!/2 function in the serializer should probably use the following check:

if decoded["event"] == "chunk" do

Where Next?

Popular in Guides/Tuts Top

niku
I have published an elixir project with using Travis CI. I would like to share some tips & thoughts that I was getting through this ...
New
slouchpie
Warmest greetings, comrades. I recently started using :dns_cluster (GitHub - phoenixframework/dns_cluster: Simple DNS clustering for dis...
New
hauks96
Hello everyone, I created a deployment tutorial for Phoenix applications with Kubernetes (microk8s) a few months back with the goal of s...
New
eclark
I’ve been working on a phoenix project lately and I wanted to use the latest versions of everything. Webpack 5 had some breaking changes ...
New
hlx
Typed my very first blog post ever, hope it will help some people https://henricus.xyz/roll-your-own-email-password-authentication-with...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
jshprentz
Geoffrey Lessel’s 2019 book, Phoenix in Action, was written for Phoenix 1.4. I found that the book’s code examples did not match the cur...
New
f0rest8
Hi, TLDR: form attribute set on the input fields and button submit. I just wanted to share a solution I discovered when making live inl...
New
AstonJ
With a few questions relating to this recently, I wonder if it might help having a thread where we share how we’ve set up Elixir/Erlang/P...
New
AstonJ
..or as and when you can think of one :icon_cool: This thread my also be of interest: What took you way too long to figure out? :003:
New

Other popular topics Top

Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New

We're in Beta

About us Mission Statement