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

tfwright
I thought I’d share a small project I’m working on to gain some familiarty with LiveView in a Phoenix app. Github Repo Deployment It’s...
New
Logan
Here is an example of a Mix application that utilizes Cowboy to handle websocket connections. If anyone has an idea about making this wor...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
WolfDan
So my main OS is Windows, I do must of my work with it, Elixir and vscode elixirls works just fine when you’re working only with elixir, ...
New
dkuku
I Created a blog post about setting up svelte with phoenix. I found it a bit tricky and the only blog post I found was written using som...
New
TwistingTwists
This is a thread to note down things/best practices encountered in LiveBeats App as I explore the source code. Found this usage of r...
New
wfgilman
I'm writing up this quick "How to" because what I thought was going to be an easy implementation of a Plug to validate a webhook request ...
New
lukertty
Install web-mode and mmm-mode first and put this in your config file: (require 'mmm-mode) (require 'web-mode) (setq mmm-global-mode 'may...
New
thetechnologyvault
One of our team members just published this getting started guide for Elixir/Phoenix devs to use the Nanobox platform: https://content.n...
New
slouchpie
Warmest greetings, comrades. I recently started using :dns_cluster (GitHub - phoenixframework/dns_cluster: Simple DNS clustering for dis...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
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
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

We're in Beta

About us Mission Statement