Deciding a way to exchange data between client and server, an overview
There are multiple approaches to exchanging data between client and server, classical client pull techniques, and modern protocols allowing server push.
A typical web application problem is exchanging data between the client and the server application with as few actions from a user as possible.
We had come far from times of old when we had to refresh a page after each interaction or when we had expected to receive some message.
Browser’s APIs have evolved over the past few years; they simplify a developer’s journey to bring users a better experience with:
Client pull techniques:
- Data CRUD (create, read, update, delete) using XHR or Fetch
– Short polling technique — sync bidirectionally based on client application events (timers and intervals)
– Long polling technique — sync bidirectionally based on client application events and partially server application events
Server push technologies :
- Server-side events (SSE) — sync from the server to the client based on the server application events
- WebSockets — sync bidirectionally based on the client and server application events.
Classical approach with modern clothes
The classical way to obtain or send data was XMLHttpRequest API (XHR). Apart from some legacy apps (see supported browser list), it’s safe to say that it’s been given new clothes in this approach with Fetch API (fetch).
XHR and fetch are HTTP requests in the background, and both might look the same from a network perspective. The difference is in the usage. Fetch API works with promises (async/await), while the old XMLHttpRequests are the way of thousand callbacks with no possible chaining other than nesting.
Long polling and short polling; different frequencies, the different trade-offs
After a user event (user redirect, button click), I can fetch/xhr each time.
The web application is loaded; at some point, as a developer, I want to send some new data from the server to the client or vice versa. I will use xhr or fetch.
We call the point an event.
There is a simple way to set up a client application event using some form of a timeout; on each timer end, I will do fetch/xhr; the technique is called short polling.
We have an excellent technique when we want to sync some data once every few minutes. If we wish to shorten intervals, there might be one significant disadvantage -> we might make many HTTP requests until some meaningful changes in data occur on the server. Or change can occur only between the interval.
We can keep requests pending for longer, assuming receiving a response is an application event; the technique is called long polling.
We fixed the problem with interval mismatching, but we might have introduced a new one; we created a more resource-intensive solution on the server and network. We might again end up with more requests and claim more resources than with short polling.
Servers can push — SSE and WebSockets.
In client applications, we often construct some artificial events, trying to observe data events that happened at the server application.
The introduction of the ‘text/event-stream’ content type to the HTTP protocol allowed the modern way to notify clients by the server -> so-called server-side events (SSE).
Browsers handle SSE by the EventSource API; server-side events might be perfect for many use cases, but they are unidirectional and have several limitations.
For example, only six connections can be active per browser; for more, see using server-sent events on MDN. See browser support; they can be polyfilled using fetch/xhr.
If we should support bidirectional real-time communication between the client and the server and optimize existing long-polling chunks in our applications, since 2011 -> introduction of RFC 6455 — The WebSocket Protocol as an upgrade of the HTTP protocol, we can use WebSockets.
Browsers implement the WebSocket API; see support. We can’t do any polyfills to support the WebSocket protocol; it must be supported by the browser when we use it. Although WebSockets might be heavy for some legacy environments or over-engineering for some solutions, it is best optimized for its purpose.
WebSocket supports binary and UTF-8 data transmission, while SSE supports only UTF-8 data transmission.
Conclusion
Each approach might be relevant to your problem. We can decide using several factors:
- How often do you want to exchange/sync data between the client and server?
– Once, go with a simple Fetch.
– Not often; (in several minutes) — in favor of short-polling.
– Often, the other three options are better. - Which way does data go — from client to server or from the server to the client?
– Only server to client — in favor of SSE
– Also, client to server; the other three options are better. - Do you want to implement different APIs for WebSockets and not only for xhr/fetch?
– Yes; consider WebSockets.
– No; the other three options are better, or only short polling and long polling is a better solution. - Do you want to support legacy server-side environments?
– Some legacy firewalls are problematic for WebSocket protocol packets - Do you want to support legacy client-side environments?
– Browsers might not support some APIs, and WebSocket API can’t be polyfilled. - Is bidirectional real-time performance critical for you (games, real-time collaborative tools, and so.)?
– Go for WebSockets. - Last but not least: do you want to invest in improving performance (is it feasible for you to improve your system’s current state)?
In other cases, you might have to do some experiments or read some resources on which server environments -> reverse proxies, and servers have the best support for which approach and design your solution.
Thank you for reading.
Deciding a way to exchange data between client and server was originally published in ableneo Technology on Medium, where people are continuing the conversation by highlighting and responding to this story.