Push APIs

We had a long discussion at work yesterday about how to build an API which supported push. By this I mean that messages originate at the server and clients, which could be other servers, browsers or apps, consume these messages.

First a bit of background, colleague Rob Blackwell has a good yardstick to test API ease of use, called ‘the cURL test‘. If your API can’t be consumed with a simple cURL command, you’ve failed. That doesn’t mean that your API is useless, it’s just hard to use, test and document.

Let’s look at a few options:

#1 Polling

Well this isn’t push at all, but it’s a common approach, and makes for a good starting point.

Pros:

  • Passes the cURL test.
  • Everything can poll.

Cons:

  • Increases the average message delivery time, proportional to poll frequency.
  • Unnecessary load on the server, serving empty poll responses.
  • Bandwidth is wasted.
  • Extra work on the client.

#2 Raw TCP Sockets

Seriously? I’m not sure sockets can really count as an API, but they will do the job.

Pros:

  • Efficient.

Cons:

  • Hard to work with, you’ll probably need to ship a client library in a variety of languages.
  • Won’t work for web browser clients
  • Fails the cURL test.
  • Unsuitable for infrequent messages to a large number of clients, as there is a cost in keeping the connection open.
  • Sockets are sometimes not available on the network (thanks to network infrastructure).

#3 Web Hooks

The concept here is that you use a normal REST API to register a URL to be called back on. When your message appears on the server, you’ll normally get an HTTP post to that URL, with the body being the message. There are a few standards lying around for web hooks, and adoption seems quite good, with GitHub as an example.

Pros:

  • Simple technology.
  • Good for infrequent messages to many clients, as TCP/IP connections do not need to be kept open.

Cons:

  • Due to the cost of establishing an outbound connection for every message, it’s unsuitable for scenarios where a high volume of messages are sent to a small number of clients.
  • Won’t work for web browser clients.
  • Fails the cURL test (you can’t get called back).

#4 Web Sockets

Using the newish HTML5 standard, a browser can upgrade a request to a server to be a full duplex communication channel. By web sockets I mean RAW web sockets. Libraries such as Socket.IO, SignalR, Faye, etc. etc… build protocols on top of web sockets, and support alternative transports.

Pros:

  • Good native support in some web browsers.

Cons:

  • The last time I checked, there wasn’t consistent support for the same web socket standard across all browsers.
  • Poor support for server-server communication, as there aren’t many client libraries for talking raw web sockets.
  • Poor support for creating a raw web socket server.
  • Unsuitable for infrequent messages to a large number of clients, as there is a cost in keeping the connection open.
  • Fails the cURL test.
  • Web sockets are sometimes not available on the network (thanks to network infrastructure).

#5 Web Socket Libraries

By this I mean the libraries that support web sockets, and degrade to a variety of exotic transports like long polling, forever frames, flash sockets, SSE etc… There are a number of these around, including Socket.IO, SignalR, Faye. I also include 3rd party services in this, such as Pusher, Azure Service Bus and various notification services in this category.

Pros:

  • Good browser support.
  • Sometimes good server support (i.e. a C# client for SignalR)
  • Handles degrading to alternative transports.

Cons:

  • Lock-in to a technology, which will severely limit client adoption.
  • Fails the cURL test.
  • Some of these libraries don’t scale well, or aren’t that ‘finished’ (I’m not naming names!).
  • This doesn’t seem like the right way to build an API.

#6 The Long Get (Comet)

I’ve seen this technique for consuming the twitter firehose, and you basically make a GET to a server, the connection is kept open, and data is streamed down to your client.

Pros:

  • Passes the cURL test!
  • Good for a high volume of messages sent to a small number of clients.
  • In theory this works in a browser (I haven’t tested it).

Cons:

  • You have to invent your own framing protocol.
  • Long running HTTP requests are terminated by network equipment in some cases. You would need retry logic.
  • Not _that_ easy to implement in all client and server technology.

Conclusion

So there you have it, there isn’t a single best way. I think your decision comes down to answering two key questions:

  1. Are you dealing with high message throughput to a small number of clients, or the occasional message to a large number of clients?
  2. Do you need to support browsers and/or servers?

Depending on these answers, I think realistically you’re looking at web hooks and the long poll as the two best options, possibly combining both of them.

What about long polling, or SSE, or something else? I don’t think many people use these technologies in their raw state, instead they form one of the possible transports in section #5.

If there’s anything I’ve missed, please let me know!

I expect in a few years time there will be a fancy new technology which solves this problem, but for now, we’ll have to make do.

(thanks to Andy Cross for the comet tip).

Advertisements