For the past 12 months I have been working on some exciting changes to Offix with the rest of the Offix team and the Offix core contributors. The offline experience has and will always be the main focus and key driver for Offix. We have been asking ourselves how we can improve on this and make this experience even better. We have decided to create the Offix DataStore and replace our previous offline client.
What is the offline experience?
Generally speaking when building your application you might often overlook consistency issues. Imagine that your user opened an existing form application and spent 2 minutes filling it in. In the meantime, someone else came and changed it.
Most of the time, developers won’t handle situations like this simply because they are hard to handle or require models that will prevent other users from making simultaneous changes. This can be even more problematic for applications that need to store data that can be presented when the client doesn’t have network connectivity.
When building offline applications most of us often focus only on reading offline data. The reason for this is that supporting offline writes requires developers to write their own replication mechanisms, which have a number of trade-offs. Thankfully projects like Offix exist and usually apply two different approaches:
- Query Cache with some levels of data normalization into entities, queries etc.
- Local database with asynchronous replication
The original Offix client used the first approach, while the Offix datastore implements the second approach.
When your cache has layers
Before we jump into what the Offix DataStore is and how we can use it, it’s necessary to give a brief preamble as to why we have made this decision as a team. Offix started as an extension to a very popular GraphQL client, Apollo, which provides out-of-the-box offline capabilities.
The Apollo Client caches GraphQL data and queries to provide state management and reduce the number of queries sent to the server. This begins to break down when the app goes offline and you need to make changes to the data.
This is where the Offix comes in. When the application goes offline, Offix schedules the mutations and adds them to an offline queue. The scheduler generates a client ID and an optimistic response is added to the optimistic layer of the cache. When the app comes back online, Offix handles the conflict resolutions and swaps out the client generated IDs for the server generated ones. All the optimistic responses are replaced with the actual data and the result is that none of the data that was created when the app went offline is lost.
This approach presents many obstacles. In the cases of more complex GraphQL types or queries, i.e. pagination and relationships, the developer has to write very complicated cache update functions which do not scale well and this results in even more boilerplate code. All in all, the experience becomes very unmanageable and the generic out the box helpers are not flexible enough to handle a vast array of use-cases. Over the years of doing offline we have received many bugs related to our Offix library and it was challenging to resolve them in the generic way without sacrificing other elements.
Limitations of the Cache and beginnings of the Offix DataStore
Our project, called Offix, used an internal API that has since been removed in Apollo GraphQL 3.0. Our team evaluated moving to the latest version of Apollo GraphQL but we felt that we could not provide user experience that was needed. This is where we started thinking about building a database-first system that works with local data first and replicates data back to the server using common protocols. This is how Offix DataStore was born. To build the ffix DataStore we needed to come up with the default protocol for exchanging data and created GraphQLCRUD, which is now being used in numerous open source technologies and projects.
Our aim is to make the offline focus as seamless as possible and not for it to be left as an afterthought. Offix generates an in-browser database based on the provided GraphQL models. This not only results in less boilerplate code, but all of the data is persisted to the browser from the start. So data is saved when the app goes offline and data is saved across app restarts.
Offix DataStore — DataSync and Replication engine
The Offix DataStore has a replication engine with delta synchronisation baked into the core, based on the GraphQL Crud specification. The Offix DataStore handles the replication of the new data to the backend automatically. The replication engine only fetches the data since the last sync (Delta synchronisation), reducing the size of the payload from the server since we are only fetching the items we need.
GraphQL Crud Specification
The Offix DataStore follows the GraphQL CRUD specification, which is implemented by Graphback, and will work out of the box with a server that follows the specification. As a result, all the queries, mutations and types can be generated, reducing boilerplate code to a minimum and resulting in quicker setup times and the ability to create quick and rapid prototypes.
State of Development
The Offix DataStore is still undergoing active development and there is still work that needs to be done around stabilising the implementation and addressing some of the limitations that are there. We have also officially made the decision to deprecate the previous Offix client.
For more information on how to get started, you can take a look at our docs.