1
votes

My apollo project is starting to get large and the problem I've been hitting is working to keep the local cache in sync with the database. For example I'll change the response of a mutation and later find out some other query was depending on the response type. This has coupled my queries together and it's getting hard to make updates.

What I would like is to run all my queries through websockets. I.E. the database is the source of truth and all data is refreshed when the websocket emits a change.

After reading this https://github.com/apollographql/subscriptions-transport-ws#full-websocket-transport I believe this is possible, but when I run a query I only get a single response and when I run a subscription I only get responses when an item changes.

Anyone know how I can leverage apollo in this way?

1

1 Answers

3
votes

I believe the feature you are asking for is called "live queries" which are not yet implemented (in 11/02/2018). For now the best is to use subscriptions to approximate to that.

What I would like is to run all my queries through websockets. I.E. the database is the source of truth and all data is refreshed when the websocket emits a change.

Let's try to go with this approach: let's say you have only 1 subscription and you will emit a notification on that subscription everytime something changes on the database.

In most use cases, people receive the modified object and integrate it manually into the local data. Your approach seems to suggest to avoid that manual integration and just refetch the entire query anew.

For that approach, you could build a High-Order-Component (HOC) that listens to that single subscription and when it emits something, the component will force a refetch of the Apollo Query. To help us, we will use helper methods that Apollo provides to allow you to do some manual work. https://www.apollographql.com/docs/react/basics/queries.html#default-result-props

Actually, the documentation from https://www.apollographql.com/docs/react/features/subscriptions.html seems out of sync with the API documentation. Therefore, I will use a method where I start a subscription without connecting it to a component.

import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import React from 'react';
import PT from 'prop-types';


//
// Create an observable to a standalone graphql subscription.
// Any component can then observe that observable.
// 

const ANYTHING_SUBSCRIPTION = gql`
    subscription onAnythingChanged() {
        onAnythingChanged { id }
    }
`;

let anythingObservable = apolloClient.queryManager.startGraphQLSubscription({
    query: ANYTHING_SUBSCRIPTION,
    variables: {},
});

//
// End of observable creation.
//


const ALL_COMMENTS_QUERY = gql`
    query AllComments() { 
        comments { id content } 
    }
`;


const withComments = graphql( ALL_COMMENTS_QUERY, { name: 'comments' } );


let Component = React.createClass({

    propTypes: {
        comments: PT.shape({
            refetch: PT.func.isRequired
        }),
    }

    componentWillMount: function () {

        let anythingSubscription = anythingObservable.subscribe({
            next: ( data ) => {
                console.log("SUBSCRIPTION EMITTED:", data );
                this.props.comments.refetch(); // Refetch comment query
            },
            error: ( err ) => {
                console.log("SUBSCRIPTION ERROR:", err );
            }
       });

       // In real code you should save anythingSubscription somewhere
       // to destroy it in the future.

    }
}


let ComponentWithCommentsAndRefetchSubscription = withComments(Component);

export default ComponentWithCommentsAndRefetchSubscription;

I hope this gives you a good starting point.

Remember that refetching all queries when anything changes is a not very efficient method. You could improve it by making the component just observe a specific category (Comments, Posts, etc.) and skip the refetch otherwise.

You can also opt to add a subscription for every component or have a global subscription somewhere in global memory (eg. Redux) that all components listen to.