Channels & Controllers

Hi,

I have started to expose some of my HTTP endpoints to a web socket channel, to provide mobile apps with a single stream to access the API.

I’ve found that there’s a lot of duplication between the two and I’m trying to work out the correct way to handle this in Elixir, with Phoenix.

In my previous, non-functional, life; I’d have had the transport specific endpoint handle the creation of a Command object and pass to a CommandBus.

Is there a functional route for this? I’m weary I’ll follow my old path into anti-patterns if I don’t ask.

NB: Has anyone else managed to expose endpoints over HTTP and WS without replicating code? Could this be done with a Plug?

Thanks.
David

2 Likes

Maybe attach some snippets of code to be understood better.
Why you want to expose HTTP endpoints to channel? Endpoints are for request/response cycle and channels are for stateful conversation. IMO You shouldn’t mix two of these.

2 Likes

I understand that web-sockets aren’t generally used for this, but there’s no reason they can’t adopt a CORRELATION_ID method, like AMQP, to handle request / response. As mobile apps rely on web-socket connections more, why would they open new connections? For further reference, http://wamp-proto.org looks cool.

I can create a struct

%RegisterUser{
    forename: "David"
}

Channel

  def handle_in("/user/create", command = %{ "identifier" => identifier, "email" => email, "metadata" => metadata}, socket) do
    with {:ok, server_identifier} <- CreateUserFunction(identifier, email, metadata)
    do
        broadcast! socket, "UserCreated", %{ "egress" => server_identifier, "ingress" => identifier}
    else
         {:noreply, socket}
    end
  end

Controller

  def create(connection, _params = %{ "email" => email, "identifier" => identifier }) do
    with {:ok, server_identifier} <- CreateUserFunction(identifier, email, metadata)
    do
      connection
        |> put_status(200)
        |> json(%{ "egress" => server_dentifier, "ingress" => identifier})
    else
      _ -> connection
          |> put_status(400)
          |> json(%{})
    end
  end

and send that to a function, from both the controller and channel functions. What I want to avoid is one being updated independently of the other. Is there a functional pattern for this?

Thanks

2 Likes

Hi,

I’m looking to do something similar but being an Elixir/Phoenix newbie (like < 2 weeks in) I’m still reading books and still feeling my way around things. I’m coming at this from starting to build APIs with FeathersJS where the real-time aspect using web-sockets is a core part of the process. I want to use Phoenix channels for the API in the same way FeathersJS does. Simple enough… and (currently) my thoughts are the same as yours. Trying not to duplicate code in the channels and controllers.

Til I get a better handle on Elixir/Phoenix I’m not too much help ATM but thought I’d chime in. But I’m glad to see someone else is on the same path. :slight_smile:

2 Likes