DEV Community

Cover image for Phoenix LiveView Meets Oban: Real-Time Interfaces Powered by Background Jobs
HexShift
HexShift

Posted on • Edited on

Phoenix LiveView Meets Oban: Real-Time Interfaces Powered by Background Jobs

Web apps rarely run in a straight line.

Uploads, emails, image processing, external API calls, PDF generation — some tasks take time.

In a synchronous world, that means waiting.

In a reactive world, frustration.

In a Phoenix LiveView world? Opportunity.

When you pair LiveView (stateful UI) with Oban (best‑in‑class Elixir job processor), you get live feedback loops that keep users informed without blocking the socket.


1. Enqueue the Work, Instantly

User submits a video to transcode:

# live_view.ex def handle_event("submit_video", %{"id" => id}, socket) do # Enqueue the heavy work %{video_id: id} |> Oban.insert(VideoTranscoder) # Immediate UI feedback {:noreply, assign(socket, status: :processing)} end 
Enter fullscreen mode Exit fullscreen mode
  • Oban.insert/1 is non‑blocking (≈ 1 ms).
  • LiveView updates the UI right away: shows Processing….

2. Subscribing to Job Updates

In mount/3, subscribe the LiveView to a PubSub topic:

def mount(%{"id" => id}, _session, socket) do topic = "video:#{id}" Phoenix.PubSub.subscribe(MyApp.PubSub, topic) {:ok, assign(socket, status: :queued, topic: topic)} end 
Enter fullscreen mode Exit fullscreen mode

3. Worker Broadcasts Progress

defmodule VideoTranscoder do use Oban.Worker, queue: :media @impl true def perform(%Oban.Job{args: %{"video_id" => id}}) do # …transcode logic… Phoenix.PubSub.broadcast(MyApp.PubSub, "video:#{id}", {:transcode_finished, id}) :ok end end 
Enter fullscreen mode Exit fullscreen mode

4. LiveView Reacts in Real Time

def handle_info({:transcode_finished, _id}, socket) do {:noreply, assign(socket, status: :done)} end 
Enter fullscreen mode Exit fullscreen mode

Result: the UI flips from Processing… to Completed the moment the job ends — no polling.


5. Handling Failures & Retries

Oban retries jobs automatically with back‑off.

Expose that state to users:

def handle_info({:transcode_failed, reason}, socket) do {:noreply, assign(socket, status: {:error, reason})} end 
Enter fullscreen mode Exit fullscreen mode

Your interface can now show:

  • “Retrying in 30 s…”
  • “Click to resubmit”
  • Error details for debugging

6. Live Dashboards, Scheduled Jobs, and More

  • Live dashboards: Stream Oban job stats into a LiveView admin panel.
  • Scheduled work: scheduled_at: DateTime.add(now, 60) to run jobs later.
  • Cancellation: Push a button → broadcast “cancel” → Oban safely discards the job.

Everything remains reactive — even future tasks.


7. Why This Works

Layer Responsibility
LiveView Render & re‑render state
Oban Run & track background work
PubSub Bridge updates to the UI

They’re independent, yet perfectly aligned:

  • LiveView never blocks.
  • Oban scales across nodes (Postgres advisory locks).
  • PubSub delivers instant status updates.

8. UX That Feels Alive

Users never ask “Is this working?” — they see it working:

  • Progress bars instead of spinners
  • Real‑time status instead of blind polling
  • Retry indicators instead of silent failure

You’re not faking immediacy; you’re rendering actual state as it evolves.


Ready to Master Phoenix LiveView?

Grab the free PDF
Phoenix LiveView: The Pro’s Guide to Scalable Interfaces and UI Patterns

  • Integrating LiveView with background jobs
  • Distributed systems patterns
  • Production‑grade UI strategies

Save weeks of guesswork and unlock Phoenix’s full potential!

Top comments (0)