How to implement Server-Sent Events in Go
Building Real-Time Applications with Efficient, Unidirectional Communication
Introduction
Server-Sent Events (SSE) is a powerful technology that enables real-time, unidirectional communication from servers to clients. In this article, we'll explore how to implement SSE in Go, discussing its benefits, use cases, and providing practical examples.
What are Server-Sent Events?
SSE is a web technology that allows servers to push data to clients over a single HTTP connection.
Unlike WebSockets, SSE is unidirectional, making it simpler to implement and ideal for scenarios where real-time updates from the server are required, but client-to-server communication is not necessary.
Developing a web application that uses SSE is straightforward. You'll need a bit of code on the server to stream events to the front-end, but the client side code works almost identically to websockets in part of handling incoming events. This is a one-way connection, so you can't send events from a client to a server.
Benefits of SSE
Simplicity: SSE is easier to implement compared to WebSockets.
Native browser support: Most modern browsers support SSE out of the box.
Automatic reconnection: Clients automatically attempt to reconnect if the connection is lost.
Efficient: Uses a single HTTP connection, reducing overhead.
Implementing SSE in Go
Let's create a simple SSE server in Go:
package main
import (
"fmt"
"net/http"
"time"
)
func sseHandler(w http.ResponseWriter, r *http.Request) {
// Set http headers required for SSE
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
// You may need this locally for CORS requests
w.Header().Set("Access-Control-Allow-Origin", "*")
// Create a channel for client disconnection
clientGone := r.Context().Done()
rc := http.NewResponseController(w)
t := time.NewTicker(time.Second)
defer t.Stop()
for {
select {
case <-clientGone:
fmt.Println("Client disconnected")
return
case <-t.C:
// Send an event to the client
// Here we send only the "data" field, but there are few others
_, err := fmt.Fprintf(w, "data: The time is %s\n\n", time.Now().Format(time.UnixDate))
if err != nil {
return
}
err = rc.Flush()
if err != nil {
return
}
}
}
}
func main() {
http.HandleFunc("/events", sseHandler)
fmt.Println("server is running on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println(err.Error())
}
}Event Stream Format
Each event is sent as a block of text terminated by a pair of newlines - \n\n
The server-side script that sends events needs to respond using the MIME type text/event-stream
You can add comments for debugging. A colon as the first character of a line is in essence a comment, and is ignored.




