By Oleksii Rudenko October 23, 2016 8:55 PM
Immediate State Updates for REST/HTTP APIs using Observer Pattern

As a consumer of enterprise APIs (especially, microservices) I want to talk about a problem that many of the APIs have nowadays — the problem of getting state updates via such APIs.

The problem is recognized mostly the API consumers and not by API developers. Consumers of such APIs have to fetch resources periodically to get the latest state and have the hard time deciding between making fewer requests (and having stale data) and making more requests (and causing more load/network activity but having up-to-date data) per time period. In this post, I would like to describe a solution to this problem and I will try to use the Pattern language for this. So meet the new pattern which I describe from the position of an API developer:

Name Immediate State Updates
Related patterns Observer Pattern
Context Resources exposed via the API are frequently changing and consumers need to process the changes almost in real-time (2-5 sec)
Problem How do you provide an efficient way of delivering resource changes via REST?

Partial solution

An endpoint (typically, GET /resource/:id) is provided to periodically check if the state of a resource has been changed. This is what you find in most APIs you work with and in the best case the endpoint is accompanied with HTTP caching. But the solution is not ideal.


Advantages Disadvantages
simplicity extra load on the system due to periodic checks made by the clients when there are no changes
clients control when and how often they want to get changes state changes don’t reach clients immediately (only after the next check)

Solution

In many cases it is possible to apply the Observer pattern to an API resource and notify clients about state changes immediately. To achieve this clients may send a list of subscription URLs which have to be invoked on any changes to the resource.

Given that my-order-service is the hostname the target API and client-service is the hostname of the consuming service, this is how the applied pattern may look like:

POST /orders HTTP/1.1
Host my-order-service

{
  "customer": "John Smith",
  "quantity": 2,
  "product": "Books",
  "subscriptions": [
    "http://client-service/order-notifications/referenceId"
  ]
}

my-order-service will notify the client-service whenever there are changes using subscriptions URLs. The notifications can be of two types:

  • Option A - stateless notifications. In this case, the consumer has to do another call to fetch the new state.

    POST /order-notifications/referenceId HTTP/1.1
    Host client-service
    
    {
      "resourceURL": "http://my-order-service/orders/10"
    }
    
  • Option B - stateful notifications. In this case, the new state is provided together with the notification

    POST /order-notifications/referenceId
    Host my-order-service
    
    {
      "resourceURL": "http://my-order-service/orders/10",
      "resource": {
        "id": 10,
        "customer": "John Smith",
        "quantity": 2,
        "product": "Books",
        "state": "IN_PROGRESS",
        "subscriptions": [
          "http://my-service/order-notifications/referenceId"
        ]
      }
    }
    

Solution Details

  • Clients can subscribe and unsubscribe using PUT/PATCH methods
  • If not only the creator of a resource can add subscriptions, a subresource called subscriptions is suggested where different clients can add their subscriptions without needed to create a resource or have a permission to modify the target resource (/resources/1/subscriptions).
  • The model of a subscription resource can be more complex and include more information about how notification has to be delivered.
  • The API can make notifications more reliable by establishing a policy for retries (e.g. up to 3 retries if the notification endpoint replied with 500)
  • The pattern does not work for the end client <-> server interactions unless there is a delivery mechanism in place for delivering push notifications to the clients such as web pages/mobile apps.

Conclusions

Ideal solution for propagating updates in near-realtime would be a lightweight messaging protocol. But already by applying the Observer pattern to a REST API, the problem can be if not solved but at least greatly mitigated. So I encourage all API designers to at least consider this pattern when designing a new API for server-to-server communication.