@livesubscriptions/apollo-client
TypeScript icon, indicating that this package has built-in type declarations

0.6.16 • Public • Published

Live Subscriptions

pipeline status coverage report licence npm version

Live Subscriptions for GraphQL. Client state easily managed by the server, only send patches on updates.

Installation

To integrate Live Subscriptions in your system you need to insert middleware in both the client and the server.

TLDR

  • Obtain the packages via npm.
  • Install middleware on client and server, both a single line change.
  • Introduce two small keywords in your schema
    • Prefix subscription with live; livePosts
    • Add liveId: String! to the toplevel type.

Apollo Client

yarn add @livesubscriptions/apollo-client

Assuming you already know how to set up Subscriptions for Apollo Client, adding support for Live Subscriptions is straightforward.

import { ApolloClient, ApolloLink, InMemoryCache } from '@apollo/client';
import { liveRequestHandlerBuilder } from '@livesubscriptions/apollo-client';

const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: ApolloLink.from([liveRequestHandlerBuilder(), splitLink]), // It's just this line (and the import)
});

Server

yarn add @livesubscriptions/node-server

When running an Express GraphQL Server installing Live Subscriptions is can be done as follows:

import { SubscriptionServer } from 'subscriptions-transport-ws';
import { execute, subscribe } from 'graphql';
import { liveSubscribeBuilder } from '@livesubscriptions/node-server';

server.listen(PORT, () => {
  new SubscriptionServer(
    {
      execute,
      subscribe: liveSubscribeBuilder(subscribe), // It's just this line (and the import)
      schema: myGraphQLSchema,
    },
    {
      server: server,
      path: '/subscriptions',
    },
  );
});

Usage

Client side there are no additional requirements to start using Live Subscriptions. Server side some minor changes are needed.

Schema

Without Live Subscriptions a regular schema looks like the schema below. A list with posts, and each Post has an Author. Suppose our backend has all capabilities to notify all clients whenever new data is present. Whenever something changes all clients receives all Posts and all Authors. So when the name of an Author changes, all Posts and all nested Authors are send to all client.

type Subscription {
  posts: [Post!]!
}

type Post {
  author: Author!
  content: string!
}

type Author {
  name: string!
}

Therefore typically this is not done with a Subscription, but with a Query and several Subscription. Merging data is left to the client.

Now see the Live Subscriptions implementation. The root of the subscription has been changed, and prefixed with live . Also a new object is introduced, with the field liveId. Everything else is the same. With just these two minor changes to the schema Live Subscriptions can start working.

type Subscription {
  livePosts: LivePosts!
}

type LivePosts {
  liveId: string!
  posts: [Post!]!
}

type Post {
  author: Author!
  content: string!
}

type Author {
  name: string!
}

When ever a part of the data changes, only the changed data is send to all clients. For this the middleware keeps track of state for each client, and uses JSON-Patch (RFC6902) standard format to create diffs. State is automatically cleaned when the websocket closes. This keeps working, even when using multiple instance of GraphQL, since websockets are tied to a single instance.

The schema requirements for Live Subscriptions can be summarized in 2 bullets:

  • The subscription must be prefixed with live; livePosts.
  • The root object must have a liveId field op type string.

Implementation

There are no additional requirements for the client, besides installing middleware.

const { data, error, loading } = useSubscription(gql`
  subscription livePosts {
    livePosts {
      liveId
      posts {
        content
        author {
          name
        }
      }
    }
  }
`);

Server side mostly relies on the middleware as well, and regular GraphQL resolvers. However, a utility class LiveManager is provided to simply managing the AsyncIterator.

export const liveManager = new LiveManager(pubSub);
liveManager.addTopic('livePosts');
pubSub.subscribe('PostUpdateEvent', () => liveManager.publish('livePosts'), {});
pubSub.subscribe('AuthorUpdateEvent', () => liveManager.publish('livePosts'), {});

Subscription: {
  livePosts: {
    // The name of the topic in the LiveManager must be the name of this field; livePosts.
    subscribe: async (parent: unknown, args: unknown, context: any) => {
      return liveManager.addSubscription({ topic: 'livePosts' }, context.user.id);
    };
  }
}
LivePosts: {
  posts: async (root: GqlLivePosts, args: unknown, context: any) => {
    /* Resolver implementations */
  };
}

Now whenever the backend inform GraphQL on new data, the resolvers will reconstruct the entire data structure per client that might be interested. The middleware will create patch files with only the diffs per client, and the middleware client side will reconstruct the data structure.

[
  {
    "OP": {
      "path": "xxx",
      "value": "yyy"
    }
  }
]

Caveats

There are some caveats to Live Subscriptions, where the main one is that your server becomes statefull. This however is without risk, since the middleware is responsible for cleaning this state once it becomes obsolete, and websockets guarentee connection to a single server. Next to that there are some more:

Misc

  • For testability in - for example - e2e tests, liveSubscribe exposes minifyLiveData.

Keywords

GraphQL, Apollo, Live Queries, Live Subscriptions

License

By David Hardy and codecentric:

The MIT License

Copyright (c) 2022 David Hardy
Copyright (c) 2022 codecentric nl

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Package Sidebar

Install

npm i @livesubscriptions/apollo-client

Weekly Downloads

1

Version

0.6.16

License

MIT

Unpacked Size

35.6 kB

Total Files

19

Last publish

Collaborators

  • endran