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
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







