How to build an Elixir chat app with Phoenix

Phoenix Framework

I recently started worked on Phoenix, an Elixir Web Framework targeting full-featured, fault tolerant applications with realtime functionality.

Here we interactively build an Elixir chat application from scratch using Phoenix’s new Websocket layer that landed in 0.2.2.

The realtime layer supports channel/topic pubsubbing, multiplexing, and ships with a javascript layer for easy client integration. The PubSub layer is based on pg2, allowing Phoenix channels to be run distributively across your entire cluster. Fault tolerance is achieved through automatic node failover.

Demo Application: phoenixchat.herokuapp.com

Example Usage

Elixir - Phoenix Router / Channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
defmodule Chat.Router do
  use Phoenix.Router
  use Phoenix.Router.Socket, mount: "/ws"

  plug Plug.Static, at: "/static", from: :chat
  get "/", Chat.Controllers.Pages, :index, as: :page

  channel "rooms", Chat.Channels.Rooms
end

defmodule Chat.Channels.Rooms do
  use Phoenix.Channel

  def join(socket, "lobby", message) do
    IO.puts "JOIN #{socket.channel}:#{socket.topic}"
    broadcast socket, "user:entered", username: message["username"]
    {:ok, socket}
  end

  def event("new:message", socket, message) do
    broadcast socket, "new:message", content: message["content"],
                                     username: messages["username"]

    socket
  end
end

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var socket = new Phoenix.Socket("ws://" + location.host + "/ws");
var $messages = $("#messages");
var $messageInput = $("#message-input");
var $usernameInput = $("#username");

socket.join("rooms", "lobby", {}, function(chan){

  chan.on("user:entered", function(message){
    $messages.append("<br/>[" + message.username + "] entered")
  });

  chan.on("new:message", function(msg){
    $messages.append("<br/>[" + msg.username + "] " + msg.content)
  });

  $messageInput.off("keypress").on("keypress", function(e){
    if(e.keyCode == 13){
      chan.send("new:message", {
        content: $messageInput.val(),
        username: $usernameInput.val()
      });
      $messageInput.val("");
    }
  });
});

Markup

1
2
3
<div id="messages"></div>
@<input id="username" type="text">
:<input id="message-input" type="text">

Comments