Building Realtime LiveView App using Phoenix PubSub

Building Realtime LiveView App using Phoenix PubSub

ยท

4 min read

In my previous blog on the Note-taking app, we missed harnessing the most important feature of Phoenix LiveView i.e real-time update. What does that mean? It means to do an update in your application screen without explicitly refreshing your application. For example, in a chat app, you don't have to explicitly refresh your screen to see the change in the screen. In our previous version of the Notetaking app when we add the note in the screen and if there is a browser screen opened parallel with the existing one then that new note won't be reflected, you have to refresh the browser to see the result. In this blog, we are going to add this new feature to our previous blog code. Refer to the gif below for the complete real-time update version of the app that we are going to make. realtime_note_app.gif

How PubSub works? ๐Ÿค”

Phoenix PubSub is an API that is responsible for doing live updates in the app. The documentation itself says it's a Realtime Publisher/Subscriber service. In application, our LiveView(module) process subscribes to a particular channel(s). When a message is published on a topic, the message is broadcast. Similarly, when a message is published from a topic, the message is broadcast to all the subscribers of that topic. When the broadcast message is received to the subscribed Process their state is updated which ultimately re-renders the view. Below diagram will give you an idea about subscribers and

Subscriber

subscriber.png

Publisher

publisher.png

Adding the Subscriber

  • We've understood the concept of the subscriber. It will be a function that will be called by the LiveView module.
  • The Phoenix.PubSub module has a subscribe function that takes 2 parameters.
    • The PubSub instance of our app, in our case it is NoteApp.PubSub
    • The name of the channel/topic, in our case it is "notes"
  • Question arises where do we place this function. Ideally, it should be placed in the context API layer but we haven't created any context API layer. So, we will create a new module lib/note_app/notes/note.ex and the following code.
    defmodule NoteApp.Notes.Note do
      def subscribe() do
         Phoenix.PubSub.subscribe(NoteApp.PubSub, "notes")
      end
    end
    

Publishing/Broadcasting the message

  • We know that PubSub has a subscribe function, likewise, it also has a broadcast function in it.
  • We have to call this function to publish/broadcast the message.
  • As per our requirement we want to publish the message(note) as soon as we create the note.
  • So our logic will reside in our create_note callback just after creating the note.
  • We need to pass the message as a tuple with the first field as the message name(as an atom) in our case it will be :create_note and the other field is the note created.

lib/note_app/notes/note_server.ex

def handle_cast({:create_note, note}, notes) do
    updated_note = add_id(notes, note)
    updates_notes = [updated_note | notes]

    message = {:note_created, updated_note}

    #broadcast logic -start
    Phoenix.PubSub.broadcast(
      NoteApp.PubSub,
      "notes",
      message
    )
    #broadcast logic -end
    {:noreply, updates_notes}
  end
  • The broadcast function takes 3 parameters.
    • The PubSub instance of our app, in our case it is NoteApp.PubSub
    • The name of the channel/topic, in our case it is "notes"
    • The message we want to broadcast.

Subscribing to the Topic/Channel

  • Any LiveView process which needs to be updated has to subscribe to the topic/channel.
  • We have already created a notes topic.
  • Our NotesLists LiveView needs to subscribe to this topic/channel.
  • This is achieved very easily, we just need to add call the subscribe function in the mount callback.
  • But there is a caveat, we can only call the subscribe function when our LiveView is connected to socket. Also, we need to alias the Note module to use lib/note_app_web/live/notes_index_live.ex
    defmodule NoteAppWeb.NotesIndexLive do
      alias NoteApp.Notes.Note
      def mount(_params, _session, socket) do
           if connected?(socket), do: Note.subscribe()
           .....
           .....
           .....
      end
    end
    
  • We know LiveView is nothing but a GenServer process, so when the publisher broadcasts the message our LiveView process needs to handle that message. We will achieve that with the handle_info callback in our NotesList LiveView module. Add the following function in the module.

lib/note_app_web/live/notes_index_live.ex

  defmodule NoteAppWeb.NotesIndexLive do
        ........
        ........
        ........

       def handle_info({:note_created, note}, socket) do
           socket = update(socket, :notes, fn notes -> [note | notes] end)
           {:noreply, socket}
      end
  end
  • We pattern match the broadcast message and retrieve the note and update the notes list in the socket with the latest message.

I hope you like this easy implementation of using Phoenix PubSub for achieving live updates. If you have any questions then please comment below.

References:

Did you find this article valuable?

Support AbulAsar S. by becoming a sponsor. Any amount is appreciated!