UserSocket using the pattern match on connect with token is failing

So the default UserSocket connect function looks like:

  def connect(_params, socket, _connect_info) do "UserSocket connect called"
    {:ok, socket}

I want to only allow connections that have a token in the params, and as per the docs I did this:

def connect(%{“token” => token}, socket) do “UserSocket connect called…” <> token
{:ok, socket}

I get an error with:

** (exit) an exception was raised:
** (FunctionClauseError) no function clause matching in RealtimeWeb.UserSocket.connect/3


Do I need the __connect_info param? I can’t seem to pattern match on the params like this either:

def connect(%{“token” => token}, socket, __connect_info)

You’ll need to have a second function clause for when connect is called without a token, the exception you’re seeing is because once you change the function params, it will only match calls where the params are a “map” with a “token” key in them, so any other call will be as if you didn’t have such a function (since the one you have doesn’t match). You’ll probably want to return an error tuple from it this second function definition.

1 Like

I had a feeling that was the problem, but it I didn’t realize that function order is also important. I moved my param extract matcher above the default one, thanks.

BTW if I wanted to do this in a single function what would be the most elegant way of performing a case on the params with a “token” key? (and returning an :error if there is none).

The Elixir way would probably to avoid any conditional when function header pattern match could be used. As mentionned above, a second function matching the request without token can solve your problem.

With a case…

def connect(params, socket, _connect_info) do
  case params do
    %{"token" => token} -> ...
    _ -> ...

With pattern matching

def connect(%{"token" => token}, socket, _connect_info), do: ...
def connect(_params, socket, _connect_info), do: ...