DEV Community

Cover image for Golang and Server-Sent Events (SSE)
Rafael Firmino
Rafael Firmino

Posted on • Edited on

Golang and Server-Sent Events (SSE)

Server-Sent Events (SSE) is a server push technology enabling a client to receive automatic updates from a server via HTTP connection, and describes how servers can initiate data transmission towards clients once an initial client connection has been established.
Server Sent Event is a good choice when we need to notify the browser.
-- wikipedia

If you're working with Amazon API Gateway and WebSocket in order to notify your clients this connection can be more expensive.

SSE can be a good choice when our application needs to send a message to it's clients.

This is a simple example how we can implementing SSE in Go.

//main.go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "math/rand"
    "net/http"
    "time"

    "github.com/gofiber/adaptor/v2"
    "github.com/gofiber/fiber/v2"
)

type Client struct {
    name   string
    events chan *DashBoard
}
type DashBoard struct {
    User uint
}

func main() {
    app := fiber.New()
    app.Get("/sse", adaptor.HTTPHandler(handler(dashboardHandler)))
    app.Listen(":3000")
}

func handler(f http.HandlerFunc) http.Handler {
    return http.HandlerFunc(f)
}
func dashboardHandler(w http.ResponseWriter, r *http.Request) {
    client := &Client{name: r.RemoteAddr, events: make(chan *DashBoard, 10)}
    go updateDashboard(client)

    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")

    timeout := time.After(1 * time.Second)
    select {
    case ev := <-client.events:
        var buf bytes.Buffer
        enc := json.NewEncoder(&buf)
        enc.Encode(ev)
        fmt.Fprintf(w, "data: %v\n\n", buf.String())
        fmt.Printf("data: %v\n", buf.String())
    case <-timeout:
        fmt.Fprintf(w, ": nothing to sent\n\n")
    }

    if f, ok := w.(http.Flusher); ok {
        f.Flush()
    }
}

func updateDashboard(client *Client) {
    for {
        db := &DashBoard{
            User: uint(rand.Uint32()),
        }
        client.events <- db
    }
}

Enter fullscreen mode Exit fullscreen mode
// JS client
const source = new EventSource("http://localhost:3000/sse")
      source.onmessage = (event) => {
        console.log("OnMessage Called:")
        console.log(event)
        console.log(JSON.parse(event.data))
      }
Enter fullscreen mode Exit fullscreen mode

Repository link

Top comments (5)

Collapse
 
der_gopher profile image
Alex Pliutau

Great write up! Does anyone use Server-Sent Events in their projects? If yes, for which use cases? This video dives into the main building blocks of Server-Sent Events in Go.
youtu.be/nvijc5J-JAQ

Collapse
 
rafaelgfirmino profile image
Rafael Firmino

Hi Alex, first of all, thanks for commenting—it’s such a pleasure to have you in the comments! 🙌 I watch a lot of your videos here in Brazil (I’m already subscribed to your channel).

Unfortunately, I don’t use SSE with Go; My company uses C#. But since I’m really into Go, I wrote this article on Go. We use SSE to handle requested file downloads, where the files take a while to be assembled and made available to our client.

Collapse
 
danwick profile image
wukong0111

Thanks for the post.
Why are you doing a keepalive every second?

Collapse
 
tuptaker profile image
Fabrizio Machado

My experience is if your server is behind a load balancer, the load balancer may sever idle connections after a short timeout, so the "no-op" keepalive gets pushed every second to prevent things like load balancers and gateways from terminating the connection.

Collapse
 
dev117uday profile image
Uday Yadav

Nice