README
AeroBase Apollo GraphQL Voyager client
Client SDK for Apollo Voyager Server
Getting Started
Importing the package
import {
createClient,
strategies
} from '@aerobase/datasync-js';
Configuration
To provide custom configuration to the client, the following options are available. If you wish, these are also available by using the DataSyncConfig
interface from the SDK.
let config: DataSyncConfig = {
httpUrl: "http://localhost:4000/graphql",
wsUrl: "ws://localhost:4000/graphql",
}
Config | Description | Default |
---|---|---|
httpUrl | The URL of your http server | N/A |
wsUrl | The URL of your websocket | N/A |
storage | The storage you want your client to use | window.localStorage |
conflictStrategy | The conflict resolution strategy your client should use | N/A |
customLinkBuilder | Enables providing custom Apollo Link for processing requests | See LinksBuilder |
networkStatus | Implementation of NetworkStatus Interface |
See WebNetworkStatus and CordovaNetworkStatus |
mutationsQueueName | The name to store requests under in your offline queue | "offline-mutation-store" |
mergeOfflineMutations | Whether or not you wish to squash mutations in your queue | true |
offlineQueueListener | listener that can be configured to receive events from offline queue | undefined |
Creating a Client
let client = createClient(config);
Example application
Try SDK using sample application: https://github.com/aerobase/apollo-voyager-ionic-example
Basic concepts
Client is basing on Apollo GraphQL client that can be used with various web and mobile frameworks. We provide version for web and Apache Cordova. For basic concepts about Apollo GraphQL please refer to documentation for your own platform.
For React: https://www.apollographql.com/docs/react/
For Angular: https://www.apollographql.com/docs/angular/
Cache
Client is strongly leveraging Apollo Cache layer. Please follow documentation for more information about caching in Apollo GraphQL
https://www.apollographql.com/docs/react/advanced/caching.html
Querying your data
Cache is used to hold data that can be fetched when client is offline.
To effectively work with cache users can use cache-first
fetchPolicy
when performing queries. This policy will try to use local cache in
situations when cache was already populated with the server side data.
return this.apollo.watchQuery<YourType>({
query: YOUR_QUERY,
fetchPolicy: 'cache-first',
});
Cache is going to be refueled by subscriptions, pooling or regular queries happening in UI.
Designing your types
When designing your GraphQL schema types id
field will be always required.
We also expect that id will be always queried back from server.
Library will perform business logic assuming that id
field will be supplied and returned from server. Without this field some offline functionalities will not work properly.
Offline support
SDK provides first class support for performing GraphQL operations while offline. Queries and mutations are hold in queue that is being configured to hold requests when client goes offline. When client goes offline for long periods of time clients will be able still negotiate local updates with the server state thanks to powerful conflict resolution strategies.
Client offers comprehensive set of features to perform data operations when offline. Thanks to offline mutation store users can stage their changes to be replicated back to server when becoming online:
Please follow chapters bellow for more information.
Querying local cache
By default client will save all performed query results in the cache.
Data will be available to be used when application goes offline.
Queries are cached out of the box based on the type and id
field.
When performing mutations that affects some queries users can use refetchQueries
or update
methods:
client.mutate<Task>({
mutation: ADD_TASK, variables: item,
optimisticResponse: createOptimisticResponse('createTask', 'Task', item),
update: this.updateCacheOnAdd
});
Making modifications when offline
AeroBase Sync SDK provides queue that stores mutations performed when offline. By default queue saves data in storage to be available after application restarts. Queue will hold requests until application will come back online.
Developers can adjust how queue will process new mutations by supplying custom NetworkStatus
implementation.
Online Only Queries
To ensure certain queries are not queued and are always delivered to the network layer, you must make use of Graphql directives. To do so on your client, ensure the query has the annotation attached like so:
exampleQuery(...) @onlineOnly {
...
}
Squashing Mutations
Multiple changes performed on the same object ID and with the same mutation will automatically be joined by the AeroBase Sync SDK when your client is offline. This is beneficial as the client will not have to queue a large amount of mutations to replay once it returns online.
Global Squashing
This feature is on by default at a global level. To disable it on a global level simply do so in your config:
let config = {
...
mergeOfflineMutations: false
...
}
Mutation Level Squashing
To disable this feature at a mutation level be sure to include the annotation on the mutation like so:
exampleMutation(...) @noSquash {
...
}
Conflicts
SDK offers way to detect conflicts that happened on the server.
Conflict strategies
You can provide your custom conflict resolution strategies to the client in the config by using the provided ConflictResolutionStrategy
type from the SDK. An example custom strategy is shown below.
let clientWins = (serverData, clientData) => {
return Object.assign(server, client);
};
To use this strategy pass this function as conflictStrategy in your config object like so:
let config = {
...
conflictStrategy: clientWins
...
}
Advanced topics
Implementing Custom Apollo Links
To use your own custom apollo links to create your own set of operation processors, simply follow the documentation for creating links here: https://www.apollographql.com/docs/link/index.html. You can pass this building mechanism to your client in the config, under the customLinkBuilder
parameter.
export const linkBuilder: LinkChainBuilder = (): ApolloLink => {
const httpLink = new HttpLink({ uri: "someUri" });
const customLink = new YourCustomLink();
let links: ApolloLink[] = [customLink, httpLink];
let compositeLink = ApolloLink.from(links);
return compositeLink;
};
Implementing Custom Network Status checks
To use your own custom network checks, implement the NetworkStatus interface which provides two functions;
onStatusChangeListener(callback: NetworkStatusChangeCallback): void;
isOffline(): boolean;
Logging debug messages
Sync package is using debug package to print out debug messages
To enable debug please execute in console
localStorage.debug = 'AeroBaseSync:*'
Some certain features can be enabled separately
localStorage.debug = 'AeroBaseSync:OfflineMutations*'
Optimistic UI
By default user changes that are made when offline will not appear in the app
until they going to be synced to server. In some circumstances users may want to see them instantly to perform various operations on the data
When performing mutations users can decide to supply optimisticResponse
object that will
appear instantly in the application UI. SDK provides helper method to work with optimistic responses.
import { createOptimisticResponse } from "@aerobase/datasync-js";
createOptimisticResponse("updateTest", "Test", { data: "test" });
Users can detect if the provided data is optimistic response by checking optimisticResponse
flag is set to true.
Note: pending changes created by helper are read only. Performing any additional operations on pending objects will result in error due to fact that next changes will be missing actual ID that can be created on server side.
Mapping Client and Server ID for Optimistic Reponses
When using OptimisticReponse
helper from SDK specific mutations that create new element response is going to have client side generated id. Subsequent edits for this objects will also refer to this id. When becoming online, all offline changes are going to be performed in specific order invalidating client side id for subsequent edits. If edits for objects created when offline are required, developers need to support a way to map them in their resolvers.
Listening to the offline queue events
Developers can implement offlineQueueListener
that can be passed as config element.
This listener is going to be notified about new items that were added to offline queue.
Listener can be used to build UI support and show pending changes.
This feature can be mixed together with OptimisticResponse
to deliver great offline experience
See example application for more information.