Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)
Web TippsUse custom web fonts in Google Sheets charts(08.09.2026 um 17:05 Uhr)
Web TippsIntroducing the new 1Password App for Google Chat(08.09.2026 um 18:02 Uhr)

🔧 Programmierung 🕛 vor 2 Monaten 4 Min Lesezeit
0

Building Loom (Part 3): Real-Time Browser UI with SSE, Goroutines, and Channels

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

This is Part 3 of my series building Loom.


👉 Missed Part 2? /
to decode every frame on the fly — no .proto files required — and streams the results into a browser UI. You see the JSON payloads, the status codes, how long each call took, and a ready-to-copy grpcurl command to replay any of them.



What it does




  • Intercepts all four gRPC stream types — unary, server-streaming, client-streaming, bidi


  • Auto-decodes using Server Reflection (no proto…













The requirement



I wanted a browser UI that shows every gRPC call in real time. No page refresh. No polling. Just instant updates.



The challenge: One incoming gRPC request needs to go to three places at once:




  • Browser UI (SSE stream)

  • Console logs

  • Recorder for replay









Why SSE over WebSockets?



WebSockets are great for two-way communication. But I just needed server → browser.



SSE advantages:




  • Simpler protocol (just HTTP)

  • Auto-reconnection built in

  • Native EventSource API in browsers

  • Perfect for "fire and forget" updates









The hub pattern



The core insight: one goroutine that owns all client connections and broadcasts to them.




CODE
type Hub struct {
clients map[chan]bool // Active connections
broadcast chan []byte // Incoming messages
register chan chan // New clients
unregister chan chan // Leaving clients
}

func (h *Hub) Run() {
for {
select {
case ch := <-h.register:
h.clients[ch] = true
case ch := <-h.unregister:
delete(h.clients, ch)
close(ch)
case msg := <-h.broadcast:
for ch := range h.clients {
ch <- msg // Send to every client
}
}
}
}







How it works: Any goroutine can push to broadcast. The hub sends it to ALL connected clients. No locks. No race conditions.



Fanning out to multiple sinks



When a gRPC request comes in, I fan it out:




CODE
func (p *Proxy) handleRequest(req *Request) {
// Same data to three places
go p.sseHub.Broadcast(req) // Browser UI
go p.logger.Log(req) // Console
go p.recorder.Record(req) // For replay

// Forward to backend
p.backend.Call(req)
}







Each sink runs in its own goroutine. If one blocks, the others keep going.



The 40KB UI file



The frontend is a single HTML file (40KB) that:



Opens an EventSource connection to /events

Listens for new gRPC calls

Renders them as cards in real time




CODE
const source = new EventSource('/events');
source.onmessage = (event) => {
const call = JSON.parse(event.data);
addCallCard(call); // Render to page
};







No React. No build step. Just vanilla JS that works.






What I learned



Channels as connection managers — The hub pattern feels unnatural at first, then becomes obvious

Fan-out is trivial in Go — go func() for each sink, done

SSE is underrated — For logs, metrics, UIs, it's perfect

One file is fine — My 40KB UI never needed splitting

Performance



With 100 concurrent gRPC requests:



Component Latency added

SSE broadcast ~2ms

Logger ~1ms

Recorder ~3ms

Total overhead ~6ms

All three run in parallel thanks to goroutines.



The aha! moment



Coming from Node.js, I would've used callbacks or promises. In Go, I just wrote:




CODE
go doSomething()
go doSomethingElse()
go doAnotherThing()






And it worked. No thinking about event loops. Just concurrency.






Key takeaways



SSE > WebSockets for one-way real-time updates

The hub pattern is Go's answer to connection management

Fan-out with goroutines is trivial — don't overthink it

Single-file UIs are fine for internal tools

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
Use custom web fonts in Google Sheets charts
2 Quellen
Introducing the new 1Password App for Google Chat
1 Quelle
Context-aware access controls are available for Gemini Enterprise in the Admin console
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Building Loom (Part 3): Real-Time Browser UI with SSE, Goroutines, and Channels

Thematisch verwandte Begriffe: Building, Loom, Part, RealTime · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...