DEV Community

Cover image for How to Connect Phoenix LiveView to External APIs Without Breaking the Real-Time Flow
HexShift
HexShift

Posted on

How to Connect Phoenix LiveView to External APIs Without Breaking the Real-Time Flow

At the heart of LiveView is a simple assumption:

Your app knows what it’s doing and can show users what matters — fast.

But what if your app doesn’t have the answer?

What if it needs to fetch it from an external API, legacy service, or third-party system?

Suddenly, you're at the mercy of latency, rate limits, and timeouts.

And still, your UI must stay reactive, responsive, and trustworthy.


The Core Problem

  • LiveView is stateful and fast
  • External APIs are stateless and slow
  • The user shouldn't have to care

So how do we make real-time UIs out of non-real-time data?


Pattern 1: Async Task + send(self(), ...)

When a user triggers an event that requires external data:

# inside handle_event/3
Task.start(fn ->
  response = ExternalAPI.get_some_data()
  send(self(), {:api_result, response})
end)

{:noreply, assign(socket, loading: true)}
Enter fullscreen mode Exit fullscreen mode

Then, handle the result:

# inside handle_info/2
def handle_info({:api_result, result}, socket) do
  {:noreply, assign(socket, loading: false, result: result)}
end
Enter fullscreen mode Exit fullscreen mode

🎯 Result: Socket stays alive, UI reflects state, no blocking.


Pattern 2: Webhook → PubSub → LiveView

If the API supports webhooks, integrate them with Phoenix PubSub:

  1. Your app receives a webhook (e.g., payment confirmation)
  2. It broadcasts to a user/topic:
   Phoenix.PubSub.broadcast(
     MyApp.PubSub,
     "user:#{user.id}",
     {:payment_confirmed, data}
   )
Enter fullscreen mode Exit fullscreen mode
  1. Your LiveView subscribed in mount/3:
   Phoenix.PubSub.subscribe(MyApp.PubSub, "user:#{user.id}")
Enter fullscreen mode Exit fullscreen mode
  1. React in handle_info/2:
   def handle_info({:payment_confirmed, data}, socket) do
     {:noreply, assign(socket, payment_status: :confirmed)}
   end
Enter fullscreen mode Exit fullscreen mode

✅ No polling. No JS. Just webhook-to-UI in real time.


Pattern 3: Cache + Background Refresh

For infrequently-changing external data:

  • On mount/3, serve cached data (ETS, Redis, etc.)
  • Kick off async refresh:
  Task.start(fn ->
    new_data = ExternalAPI.fetch(...)
    Cache.write(key, new_data)
    send(self(), {:refreshed_data, new_data})
  end)
Enter fullscreen mode Exit fullscreen mode
  • When the new data comes in, LiveView updates instantly.

Users get instant UX and fresh data, without delay.


Add Resilience

External APIs will fail.

Build for it:

Strategy Benefit
Retries + backoff Resilience to flakiness
Telemetry hooks Visibility into slow calls
Timeout messages Let users know something's wrong
Fallback UI Avoid blank screens or confusion

Show something — loading spinners, skeleton screens, retry buttons.

Truthful UIs beat fast UIs every time.


Key Principle

Real-time doesn’t mean real-instant. It means real-state.

If your app is waiting on data, show that state clearly.

Progress bars, loading text, animated skeletons — whatever suits your UX.

Let the user feel that the system is working — even when it isn’t done yet.


Your Stack for External Integration

  • Task.start for async API calls
  • handle_info for async responses
  • Phoenix.PubSub for real-time updates
  • ✅ Webhook bridges for 3rd-party systems
  • ✅ Caching to reduce perceived latency

Together, these turn any external data source into a first-class citizen in your LiveView app.


You’re Not Just Building Frontends

You’re building living reflections of distributed systems.

You orchestrate latency

You surface truth

You earn user trust


Want the Full Pattern Playbook?

Grab the PDF:

Phoenix LiveView: The Pro’s Guide to Scalable Interfaces and UI Patterns

  • Async workflows
  • External APIs
  • Distributed systems
  • Real-time feedback
  • Design patterns that scale

Use LiveView like a pro — and build UIs that truly reflect reality.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.

OSZAR »