How to connect to Elixir Channel
Good communication is as stimulating as black coffee, and just as hard.
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)
endNow 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
endHandling 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
endThank you for reading!