How to use sub topics in Phoenix Channels

Hi, I’m using Phoenix channels and am curious if I’m using them in a way that is common/correct.

I understand you’ve got the “channel”, which is a connection between a client (say a browser) and the server. And then a client can subscribe to one or many “topics” over the channel. And then the client can send “messages” regarding a topic.

For my use case, I have users that can subscribe to many topics (without authorization, simply having the topic id is good enough). The topics use the “topic:subtopic” format implied in the docs as being a useful pattern.

My topics are of the format “room:id” where id is unique and random.

In my Phoenix.Channel module I’ve parsed out requests to join particular rooms with something like:

  def join("room:" <> room_id, _message, socket) do
    %{ topic: "room:" <> room_id } = socket
    Store.get_initial_state(room_id)
    ...

And looked up the initial room state to send back to the client based on the room_id.

Then when handling messages regarding a particluar room, I parse out the particular room in question with something like:

  def handle_in("state_change:update_guests", %{"body" => body}, socket) do
    %{ topic: "room:" <> room_id } = socket
    Store.update_guests(room_id, body)
    broadcast!(socket, "state_change:update_guests", %{body: body})
    ...

Is this the correct way to deal with N unique subtopics? It works, but is there a better way? Should I be using socket "id"s?

Thanks.

Hello and welcome,

It’s the socket which is the connection, and You can multiplex many (really many) channels on it.

Under the hood, channels use distributed pubsub system.

Typical channel’s names I use

system
user:id
room:id
lobby

etc.

BTW the site uses Markdown, and if You put code, it’s better to enclose it between ```

Thanks @kokolegorille, got it. So a single socket connection multiplexes multiple channels. And channels have multiple topics.

How am I doing otherwise for my implementation of topics? Is parsing out the room id in Phoenix.Channel.join/3 the way to go? Or should it be done in the Phoenix.Socket channel macro, something like:

defmodule AppWeb.UserSocket do
  use Phoenix.Socket

  ## Channels
  channel "room:*", AppWeb.RoomChannel

Thanks.

1 Like

Parsing out the room ID there is great. I do that often. I typically parse it out in the join and put it into the assigns for use in any channel callbacks. This allows makes it a bit easier to work with.

A socket is the connection. Each socket routes specified topics to the appropriate Channel handler (Via your channel function calls in the socket module). I personally think of it as 2 physical concepts with 3 layers involved (one layer of extra abstraction). At the end of the day, the topic is literally a key in a map linking the string topic to a particular channel. From a technical perspective, you can’t have 2 of the same topic under 1 socket (maps have unique keys).

1 Like