UNPKG

6.9 kBMarkdownView Raw
1[![npm version](https://badge.fury.io/js/graphql-subscriptions.svg)](https://badge.fury.io/js/graphql-subscriptions) [![GitHub license](https://img.shields.io/github/license/apollostack/graphql-subscriptions.svg)](https://github.com/apollostack/graphql-subscriptions/blob/license/LICENSE)
2
3# graphql-subscriptions
4
5GraphQL subscriptions is a simple npm package that lets you wire up GraphQL with a pubsub system (like Redis) to implement subscriptions in GraphQL.
6
7You can use it with any GraphQL client and server (not only Apollo).
8
9### Installation
10
11`npm install graphql-subscriptions` or `yarn add graphql-subscriptions`
12
13> This package should be used with a network transport, for example [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws).
14
15### Getting started with your first subscritpion
16
17To begin with GraphQL subscriptions, start by defining a GraphQL `Subscription` type in your schema:
18
19```graphql
20type Subscription {
21 somethingChanged: Result
22}
23
24type Result {
25 id: String
26}
27```
28
29Next, add the `Subscription` type to your `schema` definition:
30
31```graphql
32schema {
33 query: Query
34 mutation: Mutation
35 subscription: Subscription
36}
37```
38
39Now, let's create a simple `PubSub` instance - it is a simple pubsub implementation, based on `EventEmitter`.
40
41To create a simple `PubSub` instance, do the following:
42
43```js
44import { PubSub } from 'graphql-subscriptions';
45
46export const pubsub = new PubSub();
47```
48
49Now, implement your Subscriptions type resolver, using the `pubsub.asyncIterator` to map the event you need:
50
51```js
52const SOMETHING_CHANGED_TOPIC = 'something_changed';
53
54export const resolvers = {
55 Subscription: {
56 somethingChanged: {
57 subscribe: () => pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC),
58 },
59 },
60}
61```
62
63> Subscriptions resolvers are not a function, but an object with `subscribe` method, than returns `AsyncIterable`.
64
65Now, GraphQL engine knows that `somethingChanged` is a subscription, and every time we will use `pubsub.publish` over this topic - is will publish it using the transport we use:
66
67```js
68pubsub.publish(SOMETHING_CHANGED_TOPIC, { somethingChanged: { id: "123" }});
69```
70
71### Filters
72
73When publishing data to subscribers, we need to make sure that each subscribers get only the data it need.
74
75To do so, we can use `withFilter` helper from this package, which wraps `AsyncItrator` with a filter function, and let you control each publication for each user.
76
77`withFilter` API:
78- `asyncIterator: AsyncIterator<any>` : Async iterator you got from your `pubsub.asyncIterator`.
79- `filterFn: (payload, variables, context, info) => boolean | Promise<boolean>` - A filter function, executed with the payload (the published value), variables, context and operation info, must return `boolean` or `Promise<boolean>` indicating if the payload should pass to the subscriber.
80
81For example, if `somethingChanged` would also accept a variable with the ID that is relevant, we can use the following code to filter according to it:
82
83```js
84import { withFilter } from 'graphql-subscriptions';
85
86const SOMETHING_CHANGED_TOPIC = 'something_changed';
87
88export const resolvers = {
89 Subscription: {
90 somethingChanged: {
91 subscribe: withFilter(pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC), (payload, variables) => {
92 return payload.somethingChanged.id === variables.relevantId;
93 }),
94 },
95 },
96}
97```
98
99> Note that when using `withFilter`, you don't need to wrap your return value with a function.
100
101### Channels Mapping
102
103You can map multiple channels into the same subscription, for example when there are multiple events that trigger the same subscription in the GraphQL engine.
104
105```js
106const SOMETHING_UPDATED = 'something_updated';
107const SOMETHING_CREATED = 'something_created';
108const SOMETHING_REMOVED = 'something_removed';
109
110export const resolvers = {
111 Subscription: {
112 somethingChanged: {
113 subscribe: () => pubsub.asyncIterator([ SOMETHING_UPDATED, SOMETHING_CREATED, SOMETHING_REMOVED ]),
114 },
115 },
116}
117````
118
119### Payload Manipulation
120
121You can also manipulate the published payload, by adding `resovle` methods to your subscription:
122
123```js
124const SOMETHING_UPDATED = 'something_updated';
125
126export const resolvers = {
127 Subscription: {
128 somethingChanged: {
129 resolve: (payload, args, context, info) => {
130 // Manipulate and return the new value
131
132 return payload;
133 },
134 subscribe: () => pubsub.asyncIterator(SOMETHING_UPDATED),
135 },
136 },
137}
138````
139
140### Custom `AsyncIterator` Wrappers
141
142The value you should return from your `subscribe` resolver must be an `AsyncIterator`.
143
144You can use this value and wrap it with another `AsyncIterator` to implement custom logic over your subscriptions.
145
146For example, the following implementation manipulate the payload by adding some static fields:
147
148```typescript
149import { $$asyncIterator } from 'iterall';
150
151export const withStaticFields = (asyncIterator: AsyncIterator<any>, staticFields: Object): Function => {
152 return (rootValue: any, args: any, context: any, info: any): AsyncIterator<any> => {
153
154 return {
155 next() {
156 return asyncIterator.next().then(({ value, done }) => {
157 return {
158 value: {
159 ...value,
160 ...staticFields,
161 },
162 done,
163 };
164 });
165 },
166 return() {
167 return Promise.resolve({ value: undefined, done: true });
168 },
169 throw(error) {
170 return Promise.reject(error);
171 },
172 [$$asyncIterator]() {
173 return this;
174 },
175 };
176 };
177};
178```
179
180> You can also take a look at `withFilter` for inspiration.
181
182For more information about `AsyncIterator`:
183- [TC39 Proposal](https://github.com/tc39/proposal-async-iteration)
184- [iterall](https://github.com/leebyron/iterall)
185- [IxJS](https://github.com/ReactiveX/IxJS)
186
187### PubSub Implementations
188
189It can be easily replaced with some other implementations of [PubSubEngine interface](https://github.com/apollographql/graphql-subscriptions/blob/master/src/pubsub.ts#L21-L25). There are a couple of them out there:
190- Use Redis with https://github.com/davidyaha/graphql-redis-subscriptions
191- Use MQTT enabled broker with https://github.com/davidyaha/graphql-mqtt-subscriptions
192- Use RabbitMQ with https://github.com/cdmbase/graphql-rabbitmq-subscriptions
193- [Add your implementation...](https://github.com/apollographql/graphql-subscriptions/pull/new/master)
194
195You can also implement a `PubSub` of your own, by using the exported interface `PubSubEngine` from this package.
196
197#### SubscriptionManager **@deprecated**
198
199`SubscriptionManager` is the previous alternative for using `graphql-js` subscriptions directly, and it's now deprecated.
200
201If you are looking for it's API docs, refer to [a previous commit of the repository](https://github.com/apollographql/graphql-subscriptions/blob/5eaee92cd50060b3f3637f00c53960f51a07d0b2/README.md)