Skip to content

Latest commit

 

History

History
171 lines (124 loc) · 9.72 KB

subscriptions.md

File metadata and controls

171 lines (124 loc) · 9.72 KB

Subscriptions

Request-Response APIs are convenient to let clients request data. However, this might be very resource-intensive. When we face these scaling problems, event-driven mechanisms can be used.

REST

RESTful Web Services by definition cannot maintain an open, stateful connection to a client. However, a number of side solutions have been built around REST-Like Web Services to provide an event-based Web API.

WebHook

The most popular approach is using WebHooks. This is simply a mechanism to let client subscribe to events by registering a callback URI. Whenever a certain event happens, the callback URI will be invoked. For example, a blog might let their API users subscribe to events, so that whenever something happens (a new article has been published or a comment has been written), the server will call each of the registered HTTP callbacks.

Sometimes, this is done manually in the developers console of each service provider, for example in GitHub or in WeChat APIs. Other service providers let developers manage programmatically their webhooks; this is supported for example by GitHub and by Microsoft Graph API.

WebHooks can also be:

  • global: callback URLs will receive every event their application is authorized to.
  • topic-based: the webhook URL will be called only under certain events. For example, notify only events of the topic orders/create. Used by Shopify.
  • parameterized: the developer is able to set parameters to select only certain notification events of the topic they are interested in. For example, to receive calendar notifications of a specific users. Used by Microsoft Graph API.

This call to the webhook could be:

  • just an event, so that the application would need to make further requests in case it wants more information.
  • A resource, containing for example the current snapshot of the resource where the event happened.

The overall design of the WebHook will depend of how the applications of the Web Service will consume that information. Give the nature of this technology, it is used to create an event-based API for servers.

Long polling and HTTP Streaming

Long polling is a technique that consists in keeping an open TCP connection between the HTTP client and the server, so that the server can decide when to respond to the client. When the server responds, a new TCP connection is opened by the client.

HTTP Streaming is quite similar: the client opens an HTTP connection to the server using a special header, Transfer-Encoding: chunked (the response will come in a series of chunks).

Note: HTTP/2 does not support chunked anymore, as it has its own mechanism stream mechanism which support concurrent streaming connections.

These techniques, in contrast to WebHooks, can be used to let HTTP clients be notified of events. For example, HTTP streaming is used by Twitter in their API to Filtered Streams.

GraphQL

GraphQL natively supports a custom operation called subscription that let a client application subscribe to a certain event. This will open a session, using the WebSocket Protocol (RFC 6455), between the server and the client app.

Subscriptions, like the rest of GraphQL root types, accepts parameters. These can be used to let app clients specify not just the topic they are interested in, but also to filter specific events of that topic.

And as with the rest of GraphQL operations, when using a subscription the client can specify the shape of the received payload.

For example, the following code can be used by a client app to subscribe to messages of type newProduct, filtering those that are of the specified type, and receiving only the id and the name of the new product:

subscription($productType: String!){
    newProduct(productType: $productType) {
        id
        name
    }
}

gRPC

Protocol Buffers implements a very powerful service definition, which allows for streaming from and to the client application, where each method (rpc) can be of any of the following types (example stubs taken from grpc.io)):

  • Unary - a regular request-response request:

    rpc SayHello(HelloRequest) returns (HelloResponse);
  • Server streaming - the client will receive a stream from the server, as in the HTTP streaming technique above.

    rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
  • Client streaming - the server will receive a stream from the client.

    rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
  • Bidirectional streaming - both ends will receive a stream from each other.

    rpc BidirectionalHello(stream HelloRequest) returns (stream HelloResponse);

An rpc with a stream response will allow to create a subscription to a topic. This mimics the HTTP/2 stream connections.

Source code

For this, any publish-subscribe solutions can be used, like Kafka, RabbitMQ or Redis Pub/Sub. Here, we will be using a basic in-memory publish-subscribe implementation provided by Apollo Server, the GraphQL framework.

A simple subscription for new articles has been created. Every API is able to react to new articles regardless of where they were originated.

REST

A simple WebHook has been added as a subscription mechanism for REST. Note that this solution is commonly seen as a companion to REST APIs, but it has nothing to do with it, so other API styles can use it as well.

Typically, registered clients or users will have a dashboard to configure their endpoint. Here, we have just added a configuration, WEBHOOK_URL, in nodemon.son. So, whenever a new article is created, a post request identifying the operation (newArticle) as well as the payload (the article itself) will be send to the expected endpoint. By default https://httpbin.org/anything is configured: as a response to any request, it will return whatever it received, which is a convenient way to test and troubleshoot.

To see how it works, just create a new article from whatever API you choose. The console of the main app will echo the payload received as a response to the remote webhook.

GraphQL

Apollo Server, one of the most popular GraphQL implementations, support subscriptions out of the box. A publish-subscribe service should be used within the server to let mutations notify the subscribers.

The sample project has been implemented using the Apollo Server middleware for Express and supports subscriptions. To run this test, we will need a client application able to use webSockets. Let's open the GraphiQL interface in http://localhost:4000/graphql.

To create a subscription, run:

subscription {
    newArticle {
        title
    }
}

Now, to create an article open the GraphiQL interface in another tab and create a new article:

mutation {
    createArticle(article:{title:"TITLE",description:"DESCRIPTION"}) {
        title
    }
}

The first GraphiQL, which is subscribed to newArticle events, would have receive the message, as in:

{
    "data": {
        "newArticle": {
            "title": "TITLE"
        }
    }
}

gRPC

Since gRPC natively supports streams, we can leverage them to provide subscriptions.

The sample project contains an endpoint to provide streaming for new articles. To create a subscription, start up the project (npm start) and the gRPC client (npm run grpcc) and then run:

client.listNewArticles({}).on('data', sr).on('status', sr);

This will create a new request to the listNewArticles rpc (that accepts an empty object). Then, it creates two handlers:

  • data, that is called whenever a new data is received. The builtin sr (shorthand of streamReply) function will be called, which will output the received data.
  • status, that is invoked whenever the status of the requests changes, for example because it has finished. Again, the builtin sr function will be called.

Now, to check how it works, we just need to create a new article. Note that it can be created from GraphQL as well. The new object will be displayed in the client gRPC application:

[email protected]:50051>
{
  "id": "5fd4d9230c6f4323c5321e98",
  "title": "This is the title",
  "description": "This is the description",
  "comments": []
}

Resources