|
| 1 | +# Subscriptions |
| 2 | + |
| 3 | +Subscriptions in GraphQL are asynchronous and event driven. Typically you need |
| 4 | +to have a callback installed, e.g. over a WebSocket connection, which receives |
| 5 | +the response to a subscribed query anytime it is updated. Since this library |
| 6 | +is protocol agnostic, it doesn't include the delivery mechanism. But it does |
| 7 | +provide a way to register callbacks when adding a subscription, and you can |
| 8 | +define trigger conditions when delivering an update to selectively dispatch |
| 9 | +the subscriptions to those listeners. |
| 10 | + |
| 11 | +## Adding/Removing a Listener |
| 12 | + |
| 13 | +Subscriptions are created or removed by calling the `Request::subscribe` |
| 14 | +and `Request::unsubscribe` methods in [GraphQLService.h](../include/GraphQLService.h): |
| 15 | +```cpp |
| 16 | +SubscriptionKey subscribe(SubscriptionParams&& params, SubscriptionCallback&& callback); |
| 17 | +void unsubscribe(SubscriptionKey key); |
| 18 | +``` |
| 19 | +You need to fill in a `SubscriptionParams` struct with the [parsed](./parsing.md) |
| 20 | +query and any other relevant operation parameters: |
| 21 | +```cpp |
| 22 | +// You can still sub-class RequestState and use that in the state parameter to Request::subscribe |
| 23 | +// to add your own state to the service callbacks that you receive while executing the subscription |
| 24 | +// query. |
| 25 | +struct SubscriptionParams |
| 26 | +{ |
| 27 | + std::shared_ptr<RequestState> state; |
| 28 | + peg::ast query; |
| 29 | + std::string operationName; |
| 30 | + response::Value variables; |
| 31 | +}; |
| 32 | +``` |
| 33 | +The `SubscriptionCallback` signature is: |
| 34 | +```cpp |
| 35 | +// Subscription callbacks receive the response::Value representing the result of evaluating the |
| 36 | +// SelectionSet against the payload. |
| 37 | +using SubscriptionCallback = std::function<void(std::future<response::Value>)>; |
| 38 | +``` |
| 39 | + |
| 40 | +## Delivering Subscription Updates |
| 41 | + |
| 42 | +There are currently three `Request::deliver` overrides you can choose from when |
| 43 | +sending updates to any subscribed listeners. The first one is the simplest, |
| 44 | +it will evaluate each subscribed query against the `subscriptionObject` |
| 45 | +parameter (which should match the `Subscription` type in the `schema`). It will |
| 46 | +unconditionally invoke every subscribed `SubscriptionCallback` callback with |
| 47 | +the response to its query: |
| 48 | +```cpp |
| 49 | +void deliver(const SubscriptionName& name, const std::shared_ptr<Object>& subscriptionObject) const; |
| 50 | +``` |
| 51 | +
|
| 52 | +The second override adds argument filtering. It will look at the field |
| 53 | +arguments in the subscription `query`, and if all of the required parameters |
| 54 | +in the `arguments` parameter are present and are an exact match it will dispatch the callback to that subscription: |
| 55 | +```cpp |
| 56 | +void deliver(const SubscriptionName& name, const SubscriptionArguments& arguments, const std::shared_ptr<Object>& subscriptionObject) const; |
| 57 | +``` |
| 58 | + |
| 59 | +The last override lets you customize the the way that the required arguments |
| 60 | +are matched. Instead of an exact match or making all of the arguments required, |
| 61 | +it will dispatch the callback if the `apply` function parameter returns true |
| 62 | +for every required field in the subscription `query`. |
| 63 | +```cpp |
| 64 | +void deliver(const SubscriptionName& name, const SubscriptionFilterCallback& apply, const std::shared_ptr<Object>& subscriptionObject) const; |
| 65 | +``` |
| 66 | +
|
| 67 | +## Handling Multiple Operation Types |
| 68 | +
|
| 69 | +Some service implementations (e.g. Apollo over HTTP) use a single pipe to |
| 70 | +manage subscriptions and resolve queries or mutations. You can't necessarily |
| 71 | +tell which operation type it is without parsing the query and searching for |
| 72 | +a specific operation name, so it's hard to tell whether you should call |
| 73 | +`resolve` or `subscribe` when the request is received that way. To help with |
| 74 | +that, there's a public `Request::findOperationDefinition` method which returns |
| 75 | +the operation type as a `std::string` along with a pointer to the AST node for |
| 76 | +the selected operation in the parsed query: |
| 77 | +```cpp |
| 78 | +std::pair<std::string, const peg::ast_node*> findOperationDefinition(const peg::ast_node& root, const std::string& operationName) const; |
| 79 | +``` |
0 commit comments