Skip to content

Server-Sent Events spec

This is a one-way communication channel; it’s ideal for listening to server events and updates to which we’ll react autonomously — that is, we won’t perform any changes on the server.

An analogy would be a television: we’re watching the information it provides, and based on that, we react by doing something in our environment. We don’t need to talk back to the TV — it informs us, and that’s enough for us to act accordingly.

A clear example would be a website that listens for stock changes. Events arrive with stock or price updates, and the application displays alerts or other information accordingly, but it doesn’t need direct communication with the server resource (the one sending the updates).

Characteristics

  • Ideal for use over the HTTP(2) protocol.
  • The native Server-Sent Events API in browsers supports header transmission and therefore OAuth security protocols.
  • No special configuration is needed for TLS implementation.
  • The native Server-Sent Events API naturally handles reconnection and other common HTTP communication aspects.
  • The Server-Sent Events specification falls under the OpenAPI umbrella: https://swagger.io/specification
  • Older browsers from 2011 can easily support SSE integration.
  • Binary data transmission is not allowed, only UTF-8 text.

WebSockets spec

This is a bidirectional communication channel — information flows both from the server to the client and vice versa, allowing the server to act upon messages sent by the client through the same channel.

Characteristics:

  • The native WebSocket API in browsers does not allow header exchange, so OAuth usage is not supported in a “standard” way.
  • Only basic authentication in the URL is supported:
var ws = new WebSocket("ws://username:password@example.com");

(Ref: https://stackoverflow.com/questions/4361173/http-headers-in-websockets-client-api) 
or implement a ticket mechanism that uses OAuth for this purpose (https://devcenter.heroku.com/articles/websocket-security).
  • The protocol is based on TCP, not HTTP. (You should check if your sidecar, such as Envoy, can support it.)
  • TLS protocol implementation over WebSockets (wss:) needs to be verified.
  • The native WebSocket API does not handle automatic reconnection; this must be implemented manually on the client side.
  • It can send and receive binary data.
  • Browsers prior to 2011 do not support WebSockets.
  • The WebSocket Protocol specification falls under the AsyncAPI umbrella: https://www.asyncapi.com/blog/websocket-part3

When to Use WebSockets or Server-Sent Events

When we receive events via either WebSockets or SSE, we must think about how we intend to react to them. When discussing reactions to events, it’s essential to distinguish the scope of that reaction.

Imagine an API that returns the stock of fruits.

curl https://{myshop}/fruits/watermelons/events

At the API semantic level:

In Server-Sent Events mode, we’ve opened a one-way listening connection to the resource http://myshop.com:8080/fruits/watermelons/events.

In WebSocket mode, we’ve opened a bidirectional connection to /fruits/watermelons/events. That means we could also send information to /fruits/watermelons/events, which semantically would mean sending events to that “watermelons” event channel.

Server-Sent Events Use Case:

Let’s continue imagining that we want to implement a rule: if the watermelon stock drops below 10, we want to send a request to the API to create more watermelons (since they’re running low).

In a REST API philosophy, our client that received the event would make a call like this to create more watermelons:

curl POST https://{myshop}/fruits/watermelons  
{"quantity": 50}

Notice that we’re invoking another resource (scope) — /fruits/watermelons, which is the correct one for creating watermelons.
Therefore, we didn’t need bidirectional communication with the /fruits/watermelons/events resource, making Server-Sent Events the most appropriate choice. WebSockets would be unnecessary and discouraged here.
Moreover, we continue using the same OpenAPI specification as our REST API.

WebSocket Use Case:

On the other hand, as mentioned earlier, WebSockets make sense when the resource we’re connected to is the one we also need to communicate with directly.
The most common, yet most illustrative, example is a chat:

GET /chats/chat23

In this case, we subscribe to events occurring in chat23, but if we want to react and send information, we’d want to send it to chat23 itself — its own conversational scope.

AsyncAPI and OpenAPI

If we choose WebSockets, we should have an AsyncAPI specification that defines what actions and payloads govern that communication.

If our server already generates an OpenAPI document for other endpoints, we should consider separating these asynchronous communication endpoints into another service (as of today, Envoy doesn’t support two specifications at once, which is why using a second, asynchronous service is recommended).

Conclusions

As we’ve seen, WebSocket can also cover the same use cases as Server-Sent Events, but from an API perspective, while SSE preserves its REST (Swagger) resources intact for any HTTP-compliant client, WebSockets require a separate asynchronous communication specification.
Additionally, clients must implement the WebSocket (WS) protocol, making maintenance and universality of the API more cumbersome.

Therefore, unless the use case explicitly requires bidirectional communication within the same scope and channel (WebSockets), Server-Sent Events are the recommended choice.

Source code and Demo