Introduction
Server-Sent Events (SSE) is a web technology that allows a server to push real-time updates to a client over HTTP. Unlike WebSockets, SSE is simpler to implement as it uses a one-way communication channel from the server to the browser and works over a regular HTTP connection. It is especially useful for applications requiring periodic updates, such as live scores, notifications, or real-time monitoring dashboards.
I created this demo because I’m currently developing an application where AIs discuss various topics. I wanted to implement some stream-like features and discovered the technology called "SSE".
Demo Overview
This demo showcases how Server-Sent Events (SSE) can be used to send real-time updates from an API to the browser. In this example, the browser displays a series of sample messages sent by the server. The simplicity of this demo makes it an excellent starting point for understanding how SSE works and integrating it into your projects.
Demo Video
Below is a video demonstrating how the Server-Sent Events (SSE) demo works in real time. Watching this video will give you a better understanding of how the client and server interact to provide real-time updates.
Implementation
The core implementation of the Server-Sent Events (SSE) demo is split into two parts: the frontend and the backend. The full source code is available on GitHub: sse-demo repository.
Frontend (ui/src/app/page.tsx
)
The frontend is built with React and provides buttons to start and stop the SSE connection, displaying real-time messages from the server. Here are the main highlights:
-
Connecting to SSE: The
handleStartConnection
function creates anEventSource
object connected to the/events
endpoint. It listens for messages, open events, and error events:-
onmessage
: Handles incoming messages and updates themessages
state. -
onopen
: Logs when the connection is established. -
onerror
: Handles errors, logging details and closing the connection if needed.
-
Stopping the Connection: The
handleStopConnection
function closes the SSE connection and cleans up.UI: The page includes a simple interface with start/stop buttons and a list to display messages.
"use client";
import type React from "react";
import { useState } from "react";
const App: React.FC = () => {
const [messages, setMessages] = useState<string[]>([]);
const [eventSource, setEventSource] = useState<EventSource | null>(null);
const handleStartConnection = () => {
const newEventSource = new EventSource("http://localhost:8080/events");
const handleOnMessage = (event: MessageEvent) => {
console.log("onmessage", event.data);
setMessages((prev) => [...prev, event.data]);
};
const handleOnOpen = () => {
console.log("Connection established");
};
const handleOnError = (error: Event) => {
console.error("onerror", error);
console.log("readyState:", newEventSource.readyState);
console.log("Connection error occurred.");
newEventSource.close();
setEventSource(null);
};
const handleOnClose = () => {
console.log("Connection is being closed by the server.");
newEventSource.close();
setEventSource(null);
};
newEventSource.onmessage = handleOnMessage;
newEventSource.onopen = handleOnOpen;
newEventSource.onerror = handleOnError;
newEventSource.addEventListener("close", handleOnClose);
setEventSource(newEventSource);
};
const handleStopConnection = () => {
if (eventSource) {
eventSource.close();
setEventSource(null);
console.log("Connection closed");
}
};
return (
<div>
<h1>Server-Sent Events Demo</h1>
<button
type="button"
onClick={handleStartConnection}
disabled={!!eventSource}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded disabled:opacity-50"
>
Start Connection
</button>
<button
type="button"
onClick={handleStopConnection}
disabled={!eventSource}
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded disabled:opacity-50 ml-2"
>
Stop Connection
</button>
<ul>
{messages.map((message, index) => (
<li key={`${index}-${message}`}>{message}</li>
))}
</ul>
</div>
);
};
export default App;
Backend (api/main.go
)
The backend is built using the Gin framework for Go and includes the following key features:
CORS Configuration: The backend uses the Gin CORS middleware to allow connections during debugging.
-
SSE Endpoint: The
/events
endpoint streams a series of pre-defined messages to the client with a delay between each message. Key components:- Headers are set to specify the
text/event-stream
content type. - Messages are sent in a loop, with a 2-second delay between each message.
- A final
close
event signals the end of the connection.
- Headers are set to specify the
package main
import (
"fmt"
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"}, // デバッグ用
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowHeaders: []string{"Origin", "Content-Type"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
}))
r.GET("/events", func(c *gin.Context) {
c.Writer.Header().Set("Content-Type", "text/event-stream")
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive")
fmt.Fprintf(c.Writer, "retry: 0\n\n")
c.Writer.Flush()
messages := []string{
"(Mike) Hello",
"(Tom) Hi!",
"(Mike) How are you?",
"(Tom) I'm good, thanks! How about you?",
"(Mike) I'm doing well, thanks for asking.",
"(Tom) What's your plan for today?",
"(Mike) I have a meeting in the afternoon.",
"(Tom) Sounds good. Let's catch up later.",
"(Mike) Sure, see you later!",
"(Tom) Bye!",
}
for _, msg := range messages {
// NOTE: This is just for demonstration purposes.
time.Sleep(2 * time.Second)
fmt.Fprintf(c.Writer, "data: %s\n\n", msg)
c.Writer.Flush()
}
c.Writer.Write([]byte("event: close\ndata: \n\n"))
c.Writer.Flush()
})
r.Run("0.0.0.0:8080")
}
How to Run It
To run this demo, please refer to the README.md file in the GitHub repository. It contains step-by-step instructions for setting up and running both the frontend and backend of the project.
Conclusion
This demo provides a simple yet effective introduction to Server-Sent Events (SSE), demonstrating how to stream real-time messages from a server to a browser. By focusing on the basics, it’s designed to help you quickly understand the core concepts and start experimenting with SSE in your own projects.
If you’re interested in trying it out or building upon this example, check out the full source code on GitHub: sse-demo repository.
Top comments (0)