Intercept and Modify LiveView Params

We’re using Hashids to obfuscate our integer ids.

In our Phoenix controllers we were able to write a simple plug to intercept the incoming hashed id and decode it into the integer id.

Here’s a simplified version of what we’re doing:

defmodule MyApp.DecodeHashIdPlug do
  def call(conn, _opts) do
    case conn.params do
      %{"id_hash" => id_hash} -> 
        %{conn | params: Map.put(params, "id", decode_id_hash!(id_hash))}

      _ -> 
        conn
     end
  end
end

defmodule MyApp.MyController do
  plug MyApp.DecodeHashIdPlug

  def show(conn, %{"id" => id}) do
    # id is the parsed integer
  end
end

Is something like this possible in LiveView?

I was hoping the new attach_hook api would let me modify the params prior to the handle_params and handle_event callbacks. However, it appears I can only return a modified socket. Is my only option to assign the parsed id to the socket?

I don’t know if the decode_id_hash! is expensive or not, but what is the reason you wouldn’t want to decode this once and have it available for use for the entire lifecycle?

Isn’t the whole point to assign params to the socket?

In the above case, assigning the decoded id to the socket makes sense since it likely wouldn’t change throughout the lifecycle. However, there are often cases where the decoding needs to happen on a per-event basis.

Take this case, where tag_id_hash is coming in, and I’d like to decode it’s value into the params tag_id:

handle_event("add_tag", %{"tag_id" => tag_id}, socket) do
  ...
end

The tag_id will be different for every event, so assigning the decoded value to the socket doesn’t really make sense.

Ideally, I’d like an interface that lets me declare which params to intercept and decode.

Something like this:

{:on_mount, DecodeHashIdHook, "tag_id_hash"}

I think attaching a hook and modifying the socket is the right approach here. You add the decoded hash to the socket on mount, and then with a hook attached to the handle_event callback you decode the new hash and update the socket. If there are multiple events in a single lifecycle, it just updates again with the same hook.