Server-Sent Event: One Directed Data Transmission Server to Client
Tags: backend, api, pipeline, tutorial
Published: 2024-05-05 00:49:26

### Preamble
SSE is unidirectional data transfer mechanism from the server to the client, that you need to perform `GET` request with `Content-Type` as `text/event-stream`, for tells the server you need to established server event channel and the server will keeps send you data changes, when you trigger an event.

---

### Use Cases

1. Live Feeds, retrieve feeds data in real time.
2. Events, retrieve data when specific event was triggered.
3. Polling, retrieve data when there is a change.

### Why not using WebSocket?
If you need bidirectional communication you can use websocket, even websocket can be used for unidirectional, but SSE is much simpler for communication with one direction (unidirectional).

---

### Make SSE Server with Go
You can just copy this code into main.go for start learning how SSE works, after that you can read the code.
```go
package main

import (
	"fmt"
	"log"
	"net/http"
)

// data variable
var messageChan chan string

func prepareHeaderForSSE(w http.ResponseWriter) {
	// prepare the header
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")
	w.Header().Set("Access-Control-Allow-Origin", "*")
}

func writeData(w http.ResponseWriter) (int, error) {
	// set data into response writer
	return fmt.Fprintf(w, "data: %s\n\n", <-messageChan)
}

func sseStream() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// call prepareHeaderForSSE for start endpoint as SSE server
		prepareHeaderForSSE(w)

		// initialize messageChan
		messageChan = make(chan string)

		// calling anonymous function that
		// closing messageChan channel and
		// set it to nil
		defer func() {
			close(messageChan)
			messageChan = nil
		}()

		// create http http.Flusher that allows
		// http handler to flush buffered data to
		// client until closed
		flusher, _ := w.(http.Flusher)
		for {
			write, err := writeData(w)
			if err != nil {
				log.Println(err)
			}
			log.Println(write)
			flusher.Flush()
		}
	}
}


// sendMessage used to write data into messageChan and flushed to client through sseStream
func sseMessage(message string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if messageChan != nil {
			messageChan <- message
		}
	}
}

func main() {
	http.HandleFunc("/stream", sseStream())
	http.HandleFunc("/send", sseMessage("you can put json in here as the data"))
	http.HandleFunc("/right", sseMessage("right"))
	http.HandleFunc("/left", sseMessage("left"))
	log.Fatal("HTTP server error: ", http.ListenAndServe("localhost:8080", nil))
}
```
Once the server successfully running, you can test it via google chrome that will pretend like client side, and listen for data through stream endpoint that we have configured as SSE stream.

---

### Using Google Chrome console for client
We use google chrome console, because its more representing server and client, (SSE) acting as the server & (google chrome console) acting as the client. you can start with this JavaScript code.

![JavaScript code for SSE Client in google chrome console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/soydqseh6oxgiq8xwde3.png)

After that you can test sending data with `/send`, `/left`, `/right` endpoint, for seeing data will be printed using console.log.

### Using cURL for Trigger the Event
#### Send event endpoint trigger
```bash
curl http://localhost:8080/send
```
#### Send event response after triggered
![Send event response in Google Chrome Console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1s2knr6u5t4bqwijwhfh.png)

#### Left event endpoint trigger
```bash
curl http://localhost:8080/left
```
#### Left event response after triggered
![Left event response in Google Chrome Console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlx3shp4sccpiqnrlx4s.png)

#### Right event endpoint trigger
```bash
curl http://localhost:8080/right
```
#### Right event response after triggered
![Right event response in Google Chrome Console](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6g2f286f9evp64632s5s.png)


---

### Conclusion
As you can see the value of data that google chrome console received from `/stream` endpoint is different every time we call 3 different endpoints, you can do the same like google chrome console does inside React.js framework using axios, or other programming language. And SSE server is only can handle one client and cannot be more, because the purpose of SSE is just to send data depends on event endpoints that we trigger through http request.

---

### My Thanks
Thank you for visiting! This article was originally published on another website. You can find the original source [here](https://0marumaru.medium.com/server-sent-event-in-golang-fe1b7d2d11e5). I'm excited to bring it over here. I hope you found it useful and enjoyable. Don't hesitate to reach out if you have any questions or feedback. Happy reading!