diff --git a/CHANGELOG.md b/CHANGELOG.md
index b36128f..88cec9e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,8 @@
# Changelog
-### 3.0.0 (not yet released)
+### 3.0.0
-- **[BREAKING]** Replace `iterall` use with native `Symbol.asyncIterator`.
+- **[BREAKING]** Replace `iterall` use with native `Symbol.asyncIterator`. `PubSubEngine.asyncIterator` is now `PubSubEngine.asyncIterableIterator`.
[@n1ru4l](https://github.com/n1ru4l) in [#232](https://github.com/apollographql/graphql-subscriptions/pull/232)
- Add an optional generic type map to `PubSub`.
[@cursorsdottsx](https://github.com/cursorsdottsx) in [#245](https://github.com/apollographql/graphql-subscriptions/pull/245)
@@ -10,11 +10,10 @@
[@rh389](https://github.com/rh389) in [#234](https://github.com/apollographql/graphql-subscriptions/pull/234)
- Support returning a Promise of an `AsyncIterator` as the `withFilter` resolver function.
[@maclockard](https://github.com/maclockard) in [#220](https://github.com/apollographql/graphql-subscriptions/pull/220)
-
-### 2.0.1 (not yet released)
-
- `withFilter` TypeScript improvements.
[@HofmannZ](https://github.com/HofmannZ) in [#230](https://github.com/apollographql/graphql-subscriptions/pull/230)
+- `withFilter` returns `AsyncIterableIterator` for compatibility with Apollo Server subscriptions.
+ [@tninesling](https://github.com/tninesling) in [#276](https://github.com/apollographql/graphql-subscriptions/pull/276)
### 2.0.0
@@ -36,77 +35,96 @@
### 1.0.0
-- BREAKING CHANGE: Changed return type of `publish`.
+- BREAKING CHANGE: Changed return type of `publish`.
[@grantwwu](https://github.com/grantwwu) in [#162](https://github.com/apollographql/graphql-subscriptions/pull/162)
- Bump versions of various devDependencies to fix security issues, use
- newer tslint config.
+ newer tslint config.
[@grantwwu](https://github.com/grantwwu) in [#163](https://github.com/apollographql/graphql-subscriptions/pull/163)
- Allows `graphql` 14 as a peer dep, forces `graphql` 14 as a dev dep, and
- has been updated to use `@types/graphql` 14.
+ has been updated to use `@types/graphql` 14.
[@hwillson](https://github.com/hwillson) in [#172](https://github.com/apollographql/graphql-subscriptions/pull/172)
### 0.5.8
+
- Bump iterall version
### 0.5.7
+
- Add `graphql@0.13` to `peerDependencies`.
### 0.5.6
+
- Add `graphql@0.12` to `peerDependencies`.
### 0.5.5
+
- FilterFn can return a Promise
- Allow passing in a custom `EventEmitter` to `PubSub`
### 0.5.4
+
- Better define `withFilter` return type [PR #111](https://github.com/apollographql/graphql-subscriptions/pull/111)
### 0.5.3
+
- Require iterall ^1.1.3 to address unhandled exceptions
### 0.5.2
+
- Require iterall ^1.1.2 to address memory leak [Issue #97] (https://github.com/apollographql/graphql-subscriptions/issues/97)
- Remove `@types/graphql` dependency. [PR #105] (https://github.com/apollographql/graphql-subscriptions/pull/105)
### 0.5.1
+
- `withFilter` now called with `(rootValue, args, context, info)` [PR #103] (https://github.com/apollographql/graphql-subscriptions/pull/103)
### 0.5.0
+
- BREAKING CHANGE: Removed deprecated code. [PR #104] (https://github.com/apollographql/graphql-subscriptions/pull/104)
- BREAKING CHANGE: Minimum GraphQL version bumped to 0.10.X. [PR #104] (https://github.com/apollographql/graphql-subscriptions/pull/104)
### 0.4.4
+
- Avoid infinite loop after the last consumer unsubscribes, [Issue #81](https://github.com/apollographql/graphql-subscriptions/issues/81) [PR #84](https://github.com/apollographql/graphql-subscriptions/pull/84)
### 0.4.3
+
- Properly propagate return() and throw() through withFilter [PR #74](https://github.com/apollographql/graphql-subscriptions/pull/74)
### 0.4.2
+
- Fixed issue with `withFilter` causing to use the same iterator [PR #69](https://github.com/apollographql/graphql-subscriptions/pull/69)
### 0.4.1
+
- Fixed exports issue with TypeScript [PR #65](https://github.com/apollographql/graphql-subscriptions/pull/65)
### 0.4.0
+
- Added `asyncIterator(channelName: string)` to `PubSub` implementation [PR #60](https://github.com/apollographql/graphql-subscriptions/pull/60)
- Added `withFilter` to allow `AsyncIterator` filtering [PR #60](https://github.com/apollographql/graphql-subscriptions/pull/60)
- Deprecate `SubscriptionManager` [PR #60](https://github.com/apollographql/graphql-subscriptions/pull/60)
- Fixed `withFilter` issue caused multiple subscribers to execute with the same AsyncIterator [PR #69](https://github.com/apollographql/graphql-subscriptions/pull/69)
### 0.3.1
+
- Add support for `defaultValue`, fixes [#49](https://github.com/apollographql/graphql-subscriptions/issues/49) (https://github.com/apollographql/graphql-subscriptions/pull/50)
### 0.3.0
+
- Allow `setupFunctions` to be async (return `Promise`) (https://github.com/apollographql/graphql-subscriptions/pull/41)
- Refactor promise chaining in pubsub engine (https://github.com/apollographql/graphql-subscriptions/pull/41)
- Fixed a possible bug with managing subscriptions internally (https://github.com/apollographql/graphql-subscriptions/pull/29)
- Return the `Promise` from `onMessage` of PubSub engine (https://github.com/apollographql/graphql-subscriptions/pull/33)
### 0.2.3
+
- update `graphql` dependency to 0.9.0
### 0.2.2
+
- made `graphql` a peer dependency and updated it to 0.8.2
### v 0.2.1
+
- Fixed a bug that caused subscriptions without operationName to fail
diff --git a/README.md b/README.md
index 7c6a907..7843d89 100644
--- a/README.md
+++ b/README.md
@@ -16,8 +16,8 @@ You can use it with any GraphQL client and server (not only Apollo).
If you are developing a project that uses this module with TypeScript:
-* ensure that your `tsconfig.json` `lib` definition includes `"es2018.asynciterable"`
-* `npm install @types/graphql` or `yarn add @types/graphql`
+- ensure that your `tsconfig.json` `lib` definition includes `"es2018.asynciterable"`
+- `npm install @types/graphql` or `yarn add @types/graphql`
### Getting started with your first subscription
@@ -47,7 +47,7 @@ Now, let's create a simple `PubSub` instance - it is a simple pubsub implementat
to the `PubSub` constructor.
```js
-import { PubSub } from 'graphql-subscriptions';
+import { PubSub } from "graphql-subscriptions";
export const pubsub = new PubSub();
```
@@ -55,35 +55,35 @@ export const pubsub = new PubSub();
If you're using TypeScript you can use the optional generic parameter for added type-safety:
```ts
-import { PubSub } from "apollo-server-express";
+import { PubSub } from "graphql-subscriptions";
const pubsub = new PubSub<{
- EVENT_ONE: { data: number; };
- EVENT_TWO: { data: string; };
+ EVENT_ONE: { data: number };
+ EVENT_TWO: { data: string };
}>();
pubsub.publish("EVENT_ONE", { data: 42 });
-pubsub.publish("EVENTONE", { data: 42 }); // ! ERROR
-pubsub.publish("EVENT_ONE", { data: "42" }); // ! ERROR
+pubsub.publish("EVENTONE", { data: 42 }); // ! ERROR
+pubsub.publish("EVENT_ONE", { data: "42" }); // ! ERROR
pubsub.publish("EVENT_TWO", { data: "hello" });
pubsub.subscribe("EVENT_ONE", () => {});
-pubsub.subscribe("EVENTONE", () => {}); // ! ERROR
+pubsub.subscribe("EVENTONE", () => {}); // ! ERROR
pubsub.subscribe("EVENT_TWO", () => {});
```
-Next implement your Subscriptions type resolver using the `pubsub.asyncIterator` to map the event you need:
+Next implement your Subscriptions type resolver using the `pubsub.asyncIterableIterator` to map the event you need:
```js
-const SOMETHING_CHANGED_TOPIC = 'something_changed';
+const SOMETHING_CHANGED_TOPIC = "something_changed";
export const resolvers = {
Subscription: {
somethingChanged: {
- subscribe: () => pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC),
+ subscribe: () => pubsub.asyncIterableIterator(SOMETHING_CHANGED_TOPIC),
},
},
-}
+};
```
> Subscriptions resolvers are not a function, but an object with `subscribe` method, that returns `AsyncIterable`.
@@ -91,7 +91,7 @@ export const resolvers = {
The GraphQL engine now knows that `somethingChanged` is a subscription, and every time we use `pubsub.publish` it will publish content using our chosen transport layer:
```js
-pubsub.publish(SOMETHING_CHANGED_TOPIC, { somethingChanged: { id: "123" }});
+pubsub.publish(SOMETHING_CHANGED_TOPIC, { somethingChanged: { id: "123" } });
```
> Note that the default PubSub implementation is intended for demo purposes. It only works if you have a single instance of your server and doesn't scale beyond a couple of connections.
@@ -104,25 +104,29 @@ When publishing data to subscribers, we need to make sure that each subscriber g
To do so, we can use `withFilter` helper from this package, which wraps `AsyncIterator` with a filter function, and lets you control each publication for each user.
`withFilter` API:
-- `asyncIteratorFn: (rootValue, args, context, info) => AsyncIterator` : A function that returns `AsyncIterator` you got from your `pubsub.asyncIterator`.
+
+- `asyncIteratorFn: (rootValue, args, context, info) => AsyncIterator` : A function that returns `AsyncIterator` you got from your `pubsub.asyncIterableIterator`.
- `filterFn: (payload, variables, context, info) => boolean | Promise` - A filter function, executed with the payload (the published value), variables, context and operation info, must return `boolean` or `Promise` indicating if the payload should pass to the subscriber.
For 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:
```js
-import { withFilter } from 'graphql-subscriptions';
+import { withFilter } from "graphql-subscriptions";
-const SOMETHING_CHANGED_TOPIC = 'something_changed';
+const SOMETHING_CHANGED_TOPIC = "something_changed";
export const resolvers = {
Subscription: {
somethingChanged: {
- subscribe: withFilter(() => pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC), (payload, variables) => {
- return payload.somethingChanged.id === variables.relevantId;
- }),
+ subscribe: withFilter(
+ () => pubsub.asyncIterableIterator(SOMETHING_CHANGED_TOPIC),
+ (payload, variables) => {
+ return payload.somethingChanged.id === variables.relevantId;
+ }
+ ),
},
},
-}
+};
```
> Note that when using `withFilter`, you don't need to wrap your return value with a function.
@@ -132,25 +136,30 @@ export const resolvers = {
You can map multiple channels into the same subscription, for example when there are multiple events that trigger the same subscription in the GraphQL engine.
```js
-const SOMETHING_UPDATED = 'something_updated';
-const SOMETHING_CREATED = 'something_created';
-const SOMETHING_REMOVED = 'something_removed';
+const SOMETHING_UPDATED = "something_updated";
+const SOMETHING_CREATED = "something_created";
+const SOMETHING_REMOVED = "something_removed";
export const resolvers = {
Subscription: {
somethingChanged: {
- subscribe: () => pubsub.asyncIterator([ SOMETHING_UPDATED, SOMETHING_CREATED, SOMETHING_REMOVED ]),
+ subscribe: () =>
+ pubsub.asyncIterableIterator([
+ SOMETHING_UPDATED,
+ SOMETHING_CREATED,
+ SOMETHING_REMOVED,
+ ]),
},
},
-}
-````
+};
+```
### Payload Manipulation
You can also manipulate the published payload, by adding `resolve` methods to your subscription:
```js
-const SOMETHING_UPDATED = 'something_updated';
+const SOMETHING_UPDATED = "something_updated";
export const resolvers = {
Subscription: {
@@ -159,27 +168,27 @@ export const resolvers = {
// Manipulate and return the new value
return payload.somethingChanged;
},
- subscribe: () => pubsub.asyncIterator(SOMETHING_UPDATED),
+ subscribe: () => pubsub.asyncIterableIterator(SOMETHING_UPDATED),
},
},
-}
-````
+};
+```
-Note that `resolve` methods execute *after* `subscribe`, so if the code in `subscribe` depends on a manipulated payload field, you will need to factor out the manipulation and call it from both `subscribe` and `resolve`.
+Note that `resolve` methods execute _after_ `subscribe`, so if the code in `subscribe` depends on a manipulated payload field, you will need to factor out the manipulation and call it from both `subscribe` and `resolve`.
### Usage with callback listeners
Your database might have callback-based listeners for changes, for example something like this:
-```JS
+```js
const listenToNewMessages = (callback) => {
- return db.table('messages').listen(newMessage => callback(newMessage));
-}
+ return db.table("messages").listen((newMessage) => callback(newMessage));
+};
// Kick off the listener
-listenToNewMessages(message => {
+listenToNewMessages((message) => {
console.log(message);
-})
+});
```
The `callback` function would be called every time a new message is saved in the database. Unfortunately, that doesn't play very well with async iterators out of the box because callbacks are push-based, where async iterators are pull-based.
@@ -187,7 +196,7 @@ The `callback` function would be called every time a new message is saved in the
We recommend using the [`callback-to-async-iterator`](https://github.com/withspectrum/callback-to-async-iterator) module to convert your callback-based listener into an async iterator:
```js
-import asyncify from 'callback-to-async-iterator';
+import asyncify from "callback-to-async-iterator";
export const resolvers = {
Subscription: {
@@ -195,23 +204,28 @@ export const resolvers = {
subscribe: () => asyncify(listenToNewMessages),
},
},
-}
-````
+};
+```
### Custom `AsyncIterator` Wrappers
-The value you should return from your `subscribe` resolver must be an `AsyncIterator`.
+The value you should return from your `subscribe` resolver must be an `AsyncIterable`.
-You can use this value and wrap it with another `AsyncIterator` to implement custom logic over your subscriptions.
+You can wrap an `AsyncIterator` with custom logic for your subscriptions. For compatibility with APIs that require `AsyncIterator` or `AsyncIterable`, your wrapper can return an `AsyncIterableIterator` to comply with both.
For example, the following implementation manipulates the payload by adding some static fields:
```typescript
-import { $$asyncIterator } from 'iterall';
-
-export const withStaticFields = (asyncIterator: AsyncIterator, staticFields: Object): Function => {
- return (rootValue: any, args: any, context: any, info: any): AsyncIterator => {
-
+export const withStaticFields = (
+ asyncIterator: AsyncIterator,
+ staticFields: Object
+): Function => {
+ return (
+ rootValue: any,
+ args: any,
+ context: any,
+ info: any
+ ): AsyncIterableIterator => {
return {
next() {
return asyncIterator.next().then(({ value, done }) => {
@@ -230,7 +244,7 @@ export const withStaticFields = (asyncIterator: AsyncIterator, staticFields
throw(error) {
return Promise.reject(error);
},
- [$$asyncIterator]() {
+ [Symbol.asyncIterator]() {
return this;
},
};
@@ -240,14 +254,10 @@ export const withStaticFields = (asyncIterator: AsyncIterator, staticFields
> You can also take a look at `withFilter` for inspiration.
-For more information about `AsyncIterator`:
-- [TC39 Proposal](https://github.com/tc39/proposal-async-iteration)
-- [iterall](https://github.com/leebyron/iterall)
-- [IxJS](https://github.com/ReactiveX/IxJS)
-
### PubSub Implementations
It can be easily replaced with some other implementations of [PubSubEngine abstract class](https://github.com/apollographql/graphql-subscriptions/blob/master/src/pubsub-engine.ts). Here are a few of them:
+
- Use Redis with https://github.com/davidyaha/graphql-redis-subscriptions
- Use Google PubSub with https://github.com/axelspringer/graphql-google-pubsub
- Use MQTT enabled broker with https://github.com/aerogear/graphql-mqtt-subscriptions