How to connect to Elixir Channel

Živković Miloš
Nov 6 · 2 min read

Good communication is as stimulating as black coffee, and just as hard.

Some channel - Photo by Sander Mathlener on Unsplash

Establishing frontend connection

socket.js is generated to create a connection to socket, it also exports connection to be used.

// channel creationlet channel = socket.channel("upload_state:all", {})// channel joiningchannel.join()
.receive("ok", data => {
console.log("Joined topic", "upload_state:all" )
})
.receive("error", resp => {
console.log("Unable to join topic", "upload_state:all")
})
// on channel event do something
channel.on("change", payload => {

channel.js file should contain the logic for connection to channel. After connection to channel is established you call the join method on channel. Event that we will listen for is called change and should be triggered on upload_state:all channel. You need to include this channel.js into your app.js file.

Security

You can use the user id as means to generating token. I am using Pow dependency to handle users, and I am happy with it. I’ve added plugs to handle the token generation. First plug attaches the user id to connection and next one generates token.

# router.ex
pipeline :browser do
...
plug :put_user_id
plug :put_token
...
end

defp put_user_id(conn, _headers) do
%{id: id} = conn
|> Pow.Plug.current_user
|> extract_user_id
assign(conn, :user_id, id)
end

defp put_token(conn, _headers) do
id = conn.assigns.user_id
first_token = Phoenix.Token.sign(SwaggUploadWeb.Endpoint, "salt", id)
assign(conn, :token, first_token)
end

Now we can extract the token and user id from conn and attach it to window object.

<!-- app.html.eex -->
<script>window.userToken = "<%= assigns[:token] %>";</script><script>window.userId = "<%= assigns[:user_id] %>";</script>

After we attached that to window we need to use it in socket.js

// socket.jslet socket = new Socket("/socket", {params: {token: window.userToken, user_id: window.userId}})

We use verify method with same salting, after that passes we get tuple with :ok atom.

# user_socket.ex
def connect(%{"token" => token, "user_id" => user_id}, socket, _connect_info) do
case Phoenix.Token.verify(SwaggUploadWeb.Endpoint, "salt", token, max_age: 86400) do
{:ok, _} ->
{:ok, assign(socket, :user_id, user_id) }
_ ->
:error
end
end

Handling Elixir channels

Mix tool has specific task in order to create channels. It gives basic instructions on how to setup your channel. You need to add your channel to socket.

# user_
channel "upload_state:*", SwaggUploadWeb.UploadStateChannel
# your_channel.ex
YourWebAppModule.Endpoint.broadcast("upload_state:all", "change", state)

Second method above broadcasts message to channel with change event. I’ve added interception in order to handle filtering. All users will get messages specific to their events. We can extract user id from socket struct and use it to compare with payload id, after they match we can push payload.

intercept ["change"]
def handle_out("change", payload, socket) do
%{id: id} = payload
case (Integer.parse(socket.assigns[:user_id])) do
^id ->
push socket, "change", payload
{:noreply, socket}
_ ->
{:noreply, socket}
end
end

Thank you for reading!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade