author
Kevin Kelche

Server-Sent Events (SSE) in Golang


Introduction

Server-Sent Events (SSE) is a unidirectional web standard that allows servers to push real-time text-based data to clients over a single, long-lived HTTP connection. Unlike polling, SSE does not require the client to make a request to the server to receive data. Instead, the client will only make a single request to the server and the server will stream data to the client as soon as they are available, reducing the amount of network traffic and latency.

SSE is a lightweight and easy-to-implement alternative to WebSockets and Long Polling. It is supported by all modern browsers and can be used to build real-time applications such as push notifications. In this tutorial, we will build a simple server that streams random numbers to the client every 2 seconds.

Prerequisites

To follow along with this tutorial, you will need to have Go installed on your machine. You can download the latest version of Go from the official website.

Creating the Server

Create two directories, one for the server and one for the client. In the server directory, create a file named main.go and add the following code:

server/main.go
package main

import (
 "fmt"
 "log"
 "math/rand"
 "net/http"
 "time"
)

func main() {
  http.Handle("/", http.FileServer(http.Dir("client")))

  http.HandleFunc("/random", randomHandler)

  log.Fatal(http.ListenAndServe(":8080", nil))
}

func randomHandler(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Access-Control-Allow-Origin", "*")
  w.Header().Set("Cache-Control", "no-cache")
  w.Header().Set("Connection", "keep-alive")
  w.Header().Set("Content-Type", "text/event-stream")

 // send a random number every 2 seconds
 for {
    rand.Seed(time.Now().UnixNano())
    fmt.Fprintf(w, "data: %d \n\n", rand.Intn(100))
    w.(http.Flusher).Flush()
    time.Sleep(2 * time.Second)
  }
}

Copied!

In the code above, we are creating a simple HTTP server that serves the client files and handles the /random endpoint. This endpoint implements the randomHandler function which sends a random number to the client every 2 seconds.

Unlike other HTTP handlers, the randomHandler function manipulates the response headers to set the Content-Type to text/event-stream . This tells the client that the server is sending a stream of events and thus the client should not close the connection after receiving the first event. Cache-Control and Connection headers are also set to prevent the client from caching the response and closing the connection. CORs headers are also set to allow the client to make requests to the server.

The randomHandler function also uses the Flusher interface to flush the response to the client after every event. This is necessary because the client will not receive any data until the response is flushed.

Creating the Client

In the client directory, create a file named index.html and add the following code:

client/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>SSE</title>
    <style type="text/css" media="screen">
      /* Add style here from the gist */
    </style>
  </head>
  <body onload="onLoaded()">
    <h1>Server Sent Events</h1>
    <div id="main">
      <h3 id="rand-container">Random Number</h3>
      <div id="random-number"></div>
    </div>
    <script>
      const onLoaded = () => {
        let eventSource = new EventSource("/random");
        eventSource.onmessage = (event) => {
          document.getElementById("random-number").innerHTML = event.data;
        };
      };
    </script>
  </body>
</html>

Copied!

The link to the styles can be found in this gist.

As you can see in the highlighted JavaScript code, instead of using the usual ajax request, we are using the EventSource object to connect to the server. The EventSource object is a built-in browser object that allows us to listen to events from the server. The onmessage event is fired whenever the server sends a new event to the client. In this case, we are updating the random-number element with the data sent by the server.

Running the Server

To run the server, in the root run the following command:

terminal
go run server/main.go

Copied!

Go to http://localhost:8080 in your browser to see the client.

It would look something like this:

Golang SSE

Conclusion

In this guide, we learned how to use Server-Sent Events in Golang. We created a simple server that streams random numbers to the client every 2 seconds. Even though this is a simple example, you can use this technique to build real-time applications such as push notifications or geolocation tracking apps. check out this article on how Shopify used SSE to simplify and scale their geolocation data streaming.

Subscribe to my newsletter

Get the latest posts delivered right to your inbox.