Implementing Push Technology Using Server-Sent Events

Share this article

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.
Making Ajax calls using the XmlHttpRequest object is a well established technique for generating dynamic server requests. However, Ajax does not allow the server to directly initiate data transfers to the client – a technique referred to as push technology. That’s where the server-sent events API comes into the picture. Specializing in push technology, server-sent events transmit data to clients as a continuous stream, referred to as an event stream, over a connection which is kept open. And, by maintaining an open connection, the overhead of repeatedly establishing a new connection is eliminated.

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 the EventSource 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 the EventSource 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, the EventSource‘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 DOM message
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 the message 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, the EventSource‘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

An EventSource 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 an EventSource 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. An EventSource will also transition into the connecting state if an established connection is lost. The readyState value for an EventSocket in the connecting state is 0. This value is defined as the constant EventSource.CONNECTING.
  • Open – An established connection is said to be in the open state. EventSource objects in the open state can receive data. A readyState value of 1 corresponds to the open state. This value is defined as the constant EventSource.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 the close() method. An EventSource in the closed state has a readyState value of 2. This value is defined as the constant EventSource.CLOSED.
The following example shows how the 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 IhrigColin Ihrig
View Author

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.

APIsIntermediateServer-Sent Events
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week