packagemain.tech

packagemain.tech

How to implement Server-Sent Events in Go

Building Real-Time Applications with Efficient, Unidirectional Communication

Julien Singler's avatar
Julien Singler
Aug 22, 2024
∙ Paid

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

  1. Simplicity: SSE is easier to implement compared to WebSockets.

  2. Native browser support: Most modern browsers support SSE out of the box.

  3. Automatic reconnection: Clients automatically attempt to reconnect if the connection is lost.

  4. 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.

User's avatar

Continue reading this post for free, courtesy of Alex Pliutau.

Or purchase a paid subscription.
© 2026 Aliaksandr Pliutau · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture