Key Takeaways
- The server-sent events API enables push technology, where data is transmitted to clients as a continuous stream over an open connection, eliminating the overhead of repeatedly establishing new connections.
- Server-sent events only allow messages to be pushed to the client from the server, unlike WebSockets which enable bidirectional communication. However, server-sent events have advantages such as support for custom message types and automatic reconnection for dropped connections.
- The client can handle various types of events from an event stream by implementing named events. Also, errors can be handled using the EventSource’s onerror event handler and an EventSource connection can be terminated by the client at any time by calling the close() method.
Comparison to WebSockets
Many people are completely unaware that server-sent events exist. This is because they are often overshadowed by the more powerful WebSockets API. While WebSockets enable bidirectional full duplex communication between the client and server, server-sent events only allow messages to be pushed to the client from the server. Applications that require near real-time performance, or two-way communication are probably better suited for WebSockets. However, server-sent events also have certain advantages over WebSockets. For example, server-sent events support custom message types and automatic reconnection for dropped connections. These features can be implemented in WebSockets, but they are available by default with server-sent events. WebSockets applications also require servers that support the WebSockets protocol. By comparison, server-sent events are built atop HTTP and can be implemented in standard web servers.Detecting Support
Server-sent events are relatively well supported, with Internet Explorer being the only major browser that does not yet support them. However, as long as IE lags behind, it will remain necessary to provide feature detection. On the client side, server-sent events are implemented using theEventSource
object – a property of the global object. The following function detects whether or not the EventSource
constructor is available in the browser. If the function returns true
, then server-sent events can be used. Otherwise, a fallback mechanism such as long polling should be used.
function supportsSSE() {
return !!window.EventSource;
}
Connecting
To connect to an event stream, call theEventSource
constructor, as shown below. You must specify the URL of the event stream that you are attempting to subscribe to. The constructor will automatically take care of opening the connection.
EventSource(url);
The onopen Event Handler
When a connection is established, theEventSource
‘s onopen
event handler is invoked. The event handler takes the open
event as its only argument. A generic onopen
event handler is shown in the following example.
source.onopen = function(event) {
// handle open event
};
EventSource
event handlers can also be written using the addEventListener()
method. This alternative syntax is preferred to onopen
because it allows multiple handlers to be attached to the same event. The previous onopen
event handler has been rewritten below, using addEventListener()
.
source.addEventListener("open", function(event) {
// handle open event
}, false);
Receiving Messages
The client interprets an event stream as a series of DOMmessage
events. Each event that is received from the server causes the EventSource
‘s onmessage
event handler to be triggered. The onmessage
handler takes a message
event as its only argument. The following example creates an onmessage
event handler.
source.onmessage = function(event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
};
The message
event contains three important properties ― data
, origin
, and lastEventId
. As the name implies, data
contains the actual message data, in string format. The data could potentially be a JSON string, which can be passed to the JSON.parse()
method. The origin
property contains the event stream’s final URL after any redirects. The origin
should be checked to verify that messages are only received from expected sources. Finally, the lastEventId
property contains the last message identifier seen in the event stream. The server can attach identifiers to individual messages using this property. If no identifier was ever seen, then lastEventId
will be the empty string.
The onmessage
event handler can also be written using the addEventListener()
method. The following example shows the previous onmessage
event handler, rewritten to use addEventListener()
.
source.addEventListener("message", function(event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
}, false);
Named Events
A single event stream can specify various types of events by implementing named events. Named events are not handled by themessage
event handler. Instead, each type of named event is processed by its own unique handler. For example, if an event stream contained events named foo
, then the following event handler would be required. Notice that the foo
event handler is identical to the message
event handler, with the exception of the event type. Of course, any other types of named messages would require separate event handlers.
source.addEventListener("foo", function(event) {
var data = event.data;
var origin = event.origin;
var lastEventId = event.lastEventId;
// handle message
}, false);
Handling Errors
If a problem occurs with the event stream, theEventSource
‘s onerror
event handler is triggered. A common cause of errors is a disrupted connection. Although the EventSource
object automatically attempts to reconnect to the server, an error event is also fired upon disconnection. The following example shows an onerror
event handler.
source.onerror = function(event) {
// handle error event
};
Of course, the onerror
event handler can also be rewritten using addEventListener()
, as shown below.
source.addEventListener("error", function(event) {
// handle error event
}, false);
Disconnecting
AnEventSource
connection can be terminated by the client at any time by calling the close()
method. The syntax for close()
is shown below. The close()
method does not take any arguments, and does not return any value.
source.close();
Connection States
The state of anEventSource
connection is stored in its readyState
property. At any point during its lifetime, a connection can be in one of three possible states – connecting, open, and closed. The following list describes each state.
- Connecting – When an
EventSource
object is created, it initially enters the connecting state. During this time, the connection is not yet established. AnEventSource
will also transition into the connecting state if an established connection is lost. ThereadyState
value for anEventSocket
in the connecting state is 0. This value is defined as the constantEventSource.CONNECTING
. - Open – An established connection is said to be in the open state.
EventSource
objects in the open state can receive data. AreadyState
value of 1 corresponds to the open state. This value is defined as the constantEventSource.OPEN
. - Closed – An
EventSource
is said to be in the closed state if a connection is not established and it is not attempting to reconnect. This state is typically entered by calling theclose()
method. AnEventSource
in the closed state has areadyState
value of 2. This value is defined as the constantEventSource.CLOSED
.
readyState
property can be used to inspect an EventSource
connection. To avoid hard coding the readyState
values, the example makes use of the state constants.
switch (source.readyState) {
case EventSource.CONNECTING:
// do something
break;
case EventSource.OPEN:
// do something
break;
case EventSource.CLOSED:
// do something
break;
default:
// this never happens
break;
}
Conclusion
This article has covered the client aspect of server-sent events. If you’re interested in learning more about server-sent events, I recommend reading The Server Side of Server-Sent Events. I’ve also written a more hands on article that covers Server-Sent Events in Node.js. Enjoy!Frequently Asked Questions (FAQs) about Implementing Push Technology Using Server-Sent Events
What are the prerequisites for implementing server-sent events?
To implement server-sent events (SSE), you need to have a basic understanding of JavaScript and Node.js. You should also be familiar with the concept of HTTP and how it works. Additionally, knowledge of event-driven programming can be beneficial as SSE is based on this concept.
How does server-sent events differ from WebSockets?
While both SSE and WebSockets provide real-time data updates, they differ in their functionality and use cases. WebSockets provide a two-way communication channel between the client and server, allowing both to send data at any time. On the other hand, SSE is a one-way communication channel where only the server can push updates to the client. This makes SSE more suitable for applications where data updates are predominantly server-initiated.
Can server-sent events work with any server-side language?
Yes, server-sent events can work with any server-side language that supports HTTP. This includes languages like Node.js, Python, PHP, Ruby, and more. The key is to set the correct HTTP headers and format the data according to the SSE specification.
How can I handle connection errors or interruptions with server-sent events?
The EventSource API, which is used to implement SSE on the client side, automatically tries to reconnect to the server when a connection is lost. You can also listen for the ‘error’ event on the EventSource object to handle connection errors or interruptions manually.
Can I send data from the client to the server using server-sent events?
No, server-sent events are designed for one-way communication from the server to the client. If you need to send data from the client to the server, you can use traditional AJAX requests or switch to a two-way communication technology like WebSockets.
Are server-sent events supported by all browsers?
Most modern browsers support server-sent events. However, Internet Explorer does not support SSE. You can use a polyfill like EventSource.js to add support for SSE in unsupported browsers.
How can I close a server-sent events connection?
You can close a SSE connection by calling the close() method on the EventSource object. This will prevent the server from sending any more updates to the client.
Can I use server-sent events for multi-user real-time applications?
Yes, you can use SSE for multi-user real-time applications. However, keep in mind that each user will open a separate connection to the server. This can lead to high server load if you have a large number of users.
How can I send different types of events with server-sent events?
You can send different types of events by including an ‘event’ field in the data sent from the server. The client can then listen for these specific event types using the addEventListener() method on the EventSource object.
Can I use server-sent events with a REST API?
Yes, you can use SSE with a REST API. The server can send updates to the client whenever a resource changes. This can be useful for keeping the client in sync with the server state without polling.
Colin Ihrig is a software engineer working primarily with Node.js. Colin is the author of Pro Node.js for Developers, and co-author of Full Stack JavaScript Development with MEAN. Colin is a member of the Node.js Technical Steering Committee, and a hapi core team member. Colin received his Bachelor of Science in Engineering, and Master of Science in Computer Engineering from the University of Pittsburgh in 2005 and 2008, respectively.