Documentation
¶
Overview ¶
Package ssr provides server-sent events (SSE) functionality for real-time updates. SSE allows the server to push updates to connected clients over a long-lived HTTP connection. This is simpler than WebSockets but perfect for server-to-client push.
The main component is the Broker which manages all connected clients and handles broadcasting messages to them. Each client connection is kept alive with periodic heartbeats to prevent timeouts from proxies and load balancers.
Usage in a handler:
broker := c.Value("broker").(*ssr.Broker)
broker.Broadcast("update", []byte(`<div>New content</div>`))
Client-side JavaScript connects to /events and listens for messages.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RenderPartial ¶
RenderPartial renders a partial template with data. This helper ensures the same HTML is used for both regular HTTP responses and SSE broadcasts, maintaining a single source of truth for fragments.
Usage:
html, err := ssr.RenderPartial(c, "partials/notification", map[string]interface{}{
"message": "Hello, world!",
})
// Use for htmx response
c.Render(200, r.HTML(html))
// AND/OR broadcast via SSE
broker.Broadcast("notification", html)
WHY: This prevents divergence between what's rendered for direct requests versus what's pushed via SSE, ensuring consistency.
Example ¶
ExampleRenderPartial shows how to use RenderPartial in practice
app := buffalo.New(buffalo.Options{})
app.POST("/api/items", func(c buffalo.Context) error {
// Process the item creation...
item := map[string]interface{}{
"id": "123",
"name": "New Item",
}
// Render the partial once
html, err := RenderPartial(c, "item_row", item)
if err != nil {
return err
}
// Use for HTMX response
if strings.Contains(c.Request().Header.Get("HX-Request"), "true") {
_, _ = c.Response().Write(html)
return nil
}
// Also broadcast via SSE to other clients
// broker.Broadcast("item-created", html)
// Or return JSON for API clients
// Or return JSON for API clients
c.Response().Header().Set("Content-Type", "application/json")
c.Response().WriteHeader(http.StatusCreated)
return nil
})
Types ¶
type Broker ¶
type Broker struct {
// contains filtered or unexported fields
}
Broker manages SSE connections and broadcasts. It's the central hub that coordinates all SSE clients, handling their lifecycle (connect/disconnect) and message distribution.
The broker runs in a separate goroutine and uses channels for thread-safe communication. This allows multiple handlers to broadcast without locks.
func NewBroker ¶
func NewBroker() *Broker
NewBroker creates a new SSE broker and starts its event loops. The broker immediately begins running in goroutines to handle:
- Client registration/unregistration
- Event broadcasting
- Heartbeat sending
You typically create one broker per app and share it across handlers:
broker := ssr.NewBroker()
app.GET("/events", broker.ServeHTTP)
func (*Broker) Broadcast ¶
Broadcast sends an event to all connected clients. This is the main API for sending real-time updates:
broker.Broadcast("notification", []byte(`<div class="alert">New message!</div>`))
The eventName allows clients to listen for specific types of updates. The html should be a rendered HTML fragment that the client-side JavaScript will insert into the DOM.
Broadcasting is non-blocking - if the broadcast channel is full, the event is dropped with a warning log. This prevents a backup of events from blocking the application.
func (*Broker) ServeHTTP ¶
ServeHTTP handles SSE connections from clients. This is a Buffalo handler that should be mounted on a GET route:
app.GET("/events", broker.ServeHTTP)
When a client connects, this handler:
- Sets appropriate SSE headers
- Creates a Client instance
- Registers the client with the broker
- Sends an initial connection event
- Enters a loop sending events until disconnect
The connection is kept open until the client disconnects or an error occurs.
type Client ¶
type Client struct {
// ID uniquely identifies this client connection.
// Generated from timestamp to ensure uniqueness.
ID string
// Events channel receives events to be sent to this client.
// Buffered to prevent slow clients from blocking the broker.
// If the buffer fills, events are dropped for that client.
Events chan Event
// Closing channel signals when the connection should be closed.
// Used for graceful shutdown of client connections.
Closing chan bool
// Response is the underlying HTTP response writer for this SSE connection.
// We write SSE-formatted data directly to this writer.
Response http.ResponseWriter
}
Client represents a connected SSE client. Each browser connection gets its own Client instance that manages the connection lifecycle and message delivery.
type Event ¶
type Event struct {
// Name is the event type. Clients can listen for specific event names.
// Common names: "message", "update", "notification", "heartbeat"
Name string
// Data is the event payload, typically HTML fragments for DOM updates.
// For Buffkit, this is usually rendered HTML that will replace elements
// on the page via JavaScript.
Data []byte
}
Event represents a server-sent event that will be sent to clients. Events have a name (event type) and data (typically HTML for live updates). The SSE protocol allows clients to listen for specific event types.