The Difference Between Client-Server and Publisher-Subscriber

APIs are designed to communicate with each other. Since this is the core purpose of web APIs, it’s easy to take for granted how that communication actually occurs. End users might unknowingly send hundreds of API requests per day and never quite understand how that exchange works in the background.

Today, we’re going to pull back the veil on this communication modality and look at two common paradigms for interconnected relationships. In this piece, we’ll discuss the standard Client-server paradigm that drives much of the most visible API experience for the average end-user. Then, we’ll compare that with Publisher-Subscriber, which is also responsible for much of the transparent data experience across the web today. We’ll look at how they function, some examples of their implementation, and how they differ.

What is the Client-Server Paradigm?

The client-server relationship, also known as request-response, is when one system that needs data requests it from a server that has it. Since the data-requesting entities are separate from the data providers, the easiest way to think about this paradigm is to imagine two callers separated by distance talking over a phone line.

When the first caller decides that they need a piece of information — for example, movie theater showtimes — they must contact the party who has this information in a standardized method. The phone system is that medium, and the technology underpinning it is standardized and implemented universally.

APIs work in a similar fashion. When a client needs data, it reaches out via a set standard as agreed upon by the data host. This communication modality carries the request (and, typically, any associated metadata for the request) to the server, which receives the request and parses the response. Once the response is formed, it is returned using the agreed-upon mode of communication.

Client-server communication has several essential characteristics. First and foremost, it is a Request-Response paradigm. No information is shared unless the user specifically requests it. The exchange is principally bi-directional, and though it’s tempting to think of it as a conversation, our example above is a bit more synchronous than what is really happening. These requests typically occur in a specific and incremental order: request, receive the request, parse the response, respond.

Client-Server-communication-nordic-apis

Client-Server

Client-Server Example

Imagine a user is going for a hike, and they want to find the best entry point amongst a series of potential entry points. The user opens their phone and navigates to a map application. Once they enter their current address, they are given the nearest entry point and ratings by other users who have gone on the trail. With the address in hand, the user then decides upon their entry point and leaves to start their hike.

This is an example of a client-server interaction. Before the user opened the app, there was no existing request or state that would prompt communication from the server. The data was there, but without a request, it was in a steady state. Once the user made their request, however, the client, in this case, an application, made a formed request for specific elements and objects stored server-side. After the server parsed the request, a response was formed and sent back to the end-user.

The following is a simple RESTful client-server request example from Oxford Dictionaries. In this request, a URL and parameter combination is used to pass a request for the word “rest” in the “English – US” language code on the “entries” resource.

https://od- api.oxforddictionaries.com/api/v2/entries/en-us/rest

While this is a straightforward example, it does expose how a simple request can provide a good deal of informational filtering capability. By properly structuring the relationship between the client and server and exposing the proper data routes for perusal, an amazing amount of functionality can be enabled.

What is the Publisher-Subscriber Paradigm?

Compared to the client-server relationship, the Publisher-Subscriber relationship is more like a delegation of responsibility. Whereas the client-server relationship is the synchronous presentation of data, publisher-subscriber is an expressed intent by the user for a future representation of that data which is then given to them upon a set of circumstances.

Let’s think back to our example of the movie theater caller wanting to know when a specific movie is showing. Suppose the user didn’t want to know about a specific time or date. Instead, they want the theater to send them a list of all their daily showings every day. This is the foundation of the publisher-subscriber relationship.

In API terms, instead of waiting for a client to request data and for a server to respond, the publisher-subscriber model has the client state that, pending certain circumstances, they’d like to receive the data as it is altered. These alterations can be something as simple as “each time this data changes, update me,” but can also be something more complex, such as “when this incremental variable reaches a number greater than 100, alert me by sending a push notification.”

Unlike client-server communication, publisher-subscriber is not centered upon an immediate request-respond paradigm. Information can be shared even if not expressly requested. The exchange is uni-directional because no request is sent after the subscription, which establishes the relationship.

Publisher-Subscriber

Publisher-Subscriber

Publisher-Subscriber Example

Let’s imagine our hiker once more. As they are preparing for their hike, they may want to know what kind of weather they will be facing. Instead of sending a formed request to a weather API, the hiker opens their phone and looks at their weather widget. Since they configured the widget weeks ago to represent the current and near-future weather state for their general area, they can see that the day is somewhat overcast and that no rain is predicted.

This is an example of the publisher-subscriber relationship. No request was made, yet the user was given the data they needed based on a previous agreement to receive that data. Without specific prompting or requesting, the weather data stream was sent to the widget for future use.

A good example of the PubSub model can be found in the documentation for Pushy, a live notification service that leans on the PubSub system to push notifications to users according to a set of parameters. In their documentation, the following request string and request body are supplied:

https://api.pushy.me/topics/subscribe?api_key=SECRET_API_KEY
{
    "token": "a6345d0278adc55d3474f5",
    "topics": [
        "news",
        "media"
    ]
}

In this request, two fields — token and topics — form a PubSub relationship. The token provided in the initial request identifies the device entering into the subscription system, and the topics field allows for a set of topics that a user may select from in order to subscribe. This is a great example of a simple but powerful call of this type. By allowing the differentiation of subscriptions to be broad and based in categories, a combination of interests, purposes, and user profiles can be supported with relatively low cost and complexity.

Purpose-Specific Implementations

These paradigms are very different, and each is best for a specific kind of implementation. Client-server is built upon the idea of request-response, and as such, is best for explicit requests in which a client desires data at the time of the request. This seems obvious, but the timing of the request is most important — when a client needs an immediate data response, the paradigm of choice is client-server, regardless of how large or complex the response in question is.

Publisher-subscriber, on the other hand, is built upon a time-delayed expression of interest. It’s exactly what the name suggests — a client subscribes to information that is then published to that user. When clients do not require immediate requests and instead need requests that are conditional to temporal, situational, or qualitative alterations, publisher-subscriber is the more appropriate paradigm.

It should be noted that these two paradigms can often be used in a way that mimics the other. It is technically possible for client-server systems to be built in such a way to mimic publisher-subscriber-like models and experiences. Similarly, you could technically build a request-like structure in publisher-subscriber systems that creates a very client-server-like experience.

We have discussed these two concepts broadly in the past in comparison to other paradigms, and the comparisons remain apt and important to consider.

REST: A stateless architecture for data transfer that is dependent on hypermedia. REST can tie together a wide range of resources that might be requested in a variety of formats for different purposes. REST is fundamentally concerned with stateless resource management, so it’s best used in such situations. Systems requiring rapid iteration and standardized HTTP verbiage will find REST best suited for their purposes.

Webhooks: Data updates to be served automatically, rather than requested. Finally, Webhooks are best used when the API in question primarily updates clients. While such APIs can also have other functions, even RESTful ones, the primary use of a Webhook microservice should be to update clients and provide updated, provisioned data upon the creation of the new, updated resource.

It should be noted that there are various protocols and implementations that can alter the way both REST and Webhooks function. Advanced GraphQL deployments can extend RESTtful design in very complex ways, opening up more responsive API design that responds based upon the need of the consumer. WebSub, formerly PubSubHubbub, was designed to replace Atom and RSS functionality using a publisher-subscriber model at its core to deliver webhooks with verification.

Client-Server and Publisher-Subscriber

Ultimately, these two communication modes are just aspects of a complete developer toolset. Ultimately, there are many domain-specific implementations, protocols, and specifications that create a complexity in choice that should not be ignored. While each has its place and proper application, developers must ensure that their systems, intent, and business logic are appropriately mirrored in the resultant API and codebase.

What do you think about our summary? Which paradigm do you prefer? Let us know below!