apollo-client
Version:
A simple yet functional GraphQL client.
362 lines • 17.1 kB
JavaScript
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
import { ApolloLink, execute, } from 'apollo-link';
import { isProduction, removeConnectionDirectiveFromDocument, } from 'apollo-utilities';
import { QueryManager } from './core/QueryManager';
import { DataStore } from './data/store';
import { version } from './version';
var hasSuggestedDevtools = false;
var supportedDirectives = new ApolloLink(function (operation, forward) {
operation.query = removeConnectionDirectiveFromDocument(operation.query);
return forward(operation);
});
/**
* This is the primary Apollo Client class. It is used to send GraphQL documents (i.e. queries
* and mutations) to a GraphQL spec-compliant server over a {@link NetworkInterface} instance,
* receive results from the server and cache the results in a store. It also delivers updates
* to GraphQL queries through {@link Observable} instances.
*/
var ApolloClient = /** @class */ (function () {
/**
* Constructs an instance of {@link ApolloClient}.
*
* @param link The {@link ApolloLink} over which GraphQL documents will be resolved into a response.
*
* @param cache The initial cache to use in the data store.
*
* @param ssrMode Determines whether this is being run in Server Side Rendering (SSR) mode.
*
* @param ssrForceFetchDelay Determines the time interval before we force fetch queries for a
* server side render.
*
* @param queryDeduplication If set to false, a query will still be sent to the server even if a query
* with identical parameters (query, variables, operationName) is already in flight.
*
*/
function ApolloClient(options) {
var _this = this;
this.defaultOptions = {};
this.resetStoreCallbacks = [];
var link = options.link, cache = options.cache, _a = options.ssrMode, ssrMode = _a === void 0 ? false : _a, _b = options.ssrForceFetchDelay, ssrForceFetchDelay = _b === void 0 ? 0 : _b, connectToDevTools = options.connectToDevTools, _c = options.queryDeduplication, queryDeduplication = _c === void 0 ? true : _c, defaultOptions = options.defaultOptions;
if (!link || !cache) {
throw new Error("\n In order to initialize Apollo Client, you must specify link & cache properties on the config object.\n This is part of the required upgrade when migrating from Apollo Client 1.0 to Apollo Client 2.0.\n For more information, please visit:\n https://www.apollographql.com/docs/react/basics/setup.html\n to help you get started.\n ");
}
// remove apollo-client supported directives
this.link = supportedDirectives.concat(link);
this.cache = cache;
this.store = new DataStore(cache);
this.disableNetworkFetches = ssrMode || ssrForceFetchDelay > 0;
this.queryDeduplication = queryDeduplication;
this.ssrMode = ssrMode;
this.defaultOptions = defaultOptions || {};
if (ssrForceFetchDelay) {
setTimeout(function () { return (_this.disableNetworkFetches = false); }, ssrForceFetchDelay);
}
this.watchQuery = this.watchQuery.bind(this);
this.query = this.query.bind(this);
this.mutate = this.mutate.bind(this);
this.resetStore = this.resetStore.bind(this);
this.reFetchObservableQueries = this.reFetchObservableQueries.bind(this);
// Attach the client instance to window to let us be found by chrome devtools, but only in
// development mode
var defaultConnectToDevTools = !isProduction() &&
typeof window !== 'undefined' &&
!window.__APOLLO_CLIENT__;
if (typeof connectToDevTools === 'undefined'
? defaultConnectToDevTools
: connectToDevTools && typeof window !== 'undefined') {
window.__APOLLO_CLIENT__ = this;
}
/**
* Suggest installing the devtools for developers who don't have them
*/
if (!hasSuggestedDevtools && !isProduction()) {
hasSuggestedDevtools = true;
if (typeof window !== 'undefined' &&
window.document &&
window.top === window.self) {
// First check if devtools is not installed
if (typeof window.__APOLLO_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
// Only for Chrome
if (window.navigator &&
window.navigator.userAgent.indexOf('Chrome') > -1) {
// tslint:disable-next-line
console.debug('Download the Apollo DevTools ' +
'for a better development experience: ' +
'https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm');
}
}
}
}
this.version = version;
}
/**
* This watches the results of the query according to the options specified and
* returns an {@link ObservableQuery}. We can subscribe to this {@link ObservableQuery} and
* receive updated results through a GraphQL observer.
* <p /><p />
* Note that this method is not an implementation of GraphQL subscriptions. Rather,
* it uses Apollo's store in order to reactively deliver updates to your query results.
* <p /><p />
* For example, suppose you call watchQuery on a GraphQL query that fetches an person's
* first name and last name and this person has a particular object identifer, provided by
* dataIdFromObject. Later, a different query fetches that same person's
* first and last name and his/her first name has now changed. Then, any observers associated
* with the results of the first query will be updated with a new result object.
* <p /><p />
* See [here](https://medium.com/apollo-stack/the-concepts-of-graphql-bc68bd819be3#.3mb0cbcmc) for
* a description of store reactivity.
*
*/
ApolloClient.prototype.watchQuery = function (options) {
this.initQueryManager();
if (this.defaultOptions.watchQuery) {
options = __assign({}, this.defaultOptions.watchQuery, options);
}
// XXX Overwriting options is probably not the best way to do this long term...
if (this.disableNetworkFetches &&
(options.fetchPolicy === 'network-only' ||
options.fetchPolicy === 'cache-and-network')) {
options = __assign({}, options, { fetchPolicy: 'cache-first' });
}
return this.queryManager.watchQuery(options);
};
/**
* This resolves a single query according to the options specified and
* returns a {@link Promise} which is either resolved with the resulting data
* or rejected with an error.
*
* @param options An object of type {@link QueryOptions} that allows us to
* describe how this query should be treated e.g. whether it should hit the
* server at all or just resolve from the cache, etc.
*/
ApolloClient.prototype.query = function (options) {
this.initQueryManager();
if (this.defaultOptions.query) {
options = __assign({}, this.defaultOptions.query, options);
}
if (options.fetchPolicy === 'cache-and-network') {
throw new Error('cache-and-network fetchPolicy can only be used with watchQuery');
}
// XXX Overwriting options is probably not the best way to do this long
// term...
if (this.disableNetworkFetches && options.fetchPolicy === 'network-only') {
options = __assign({}, options, { fetchPolicy: 'cache-first' });
}
return this.queryManager.query(options);
};
/**
* This resolves a single mutation according to the options specified and returns a
* {@link Promise} which is either resolved with the resulting data or rejected with an
* error.
*
* It takes options as an object with the following keys and values:
*/
ApolloClient.prototype.mutate = function (options) {
this.initQueryManager();
if (this.defaultOptions.mutate) {
options = __assign({}, this.defaultOptions.mutate, options);
}
return this.queryManager.mutate(options);
};
/**
* This subscribes to a graphql subscription according to the options specified and returns an
* {@link Observable} which either emits received data or an error.
*/
ApolloClient.prototype.subscribe = function (options) {
this.initQueryManager();
return this.queryManager.startGraphQLSubscription(options);
};
/**
* Tries to read some data from the store in the shape of the provided
* GraphQL query without making a network request. This method will start at
* the root query. To start at a specific id returned by `dataIdFromObject`
* use `readFragment`.
*/
ApolloClient.prototype.readQuery = function (options) {
return this.initProxy().readQuery(options);
};
/**
* Tries to read some data from the store in the shape of the provided
* GraphQL fragment without making a network request. This method will read a
* GraphQL fragment from any arbitrary id that is currently cached, unlike
* `readQuery` which will only read from the root query.
*
* You must pass in a GraphQL document with a single fragment or a document
* with multiple fragments that represent what you are reading. If you pass
* in a document with multiple fragments then you must also specify a
* `fragmentName`.
*/
ApolloClient.prototype.readFragment = function (options) {
return this.initProxy().readFragment(options);
};
/**
* Writes some data in the shape of the provided GraphQL query directly to
* the store. This method will start at the root query. To start at a a
* specific id returned by `dataIdFromObject` then use `writeFragment`.
*/
ApolloClient.prototype.writeQuery = function (options) {
var result = this.initProxy().writeQuery(options);
this.queryManager.broadcastQueries();
return result;
};
/**
* Writes some data in the shape of the provided GraphQL fragment directly to
* the store. This method will write to a GraphQL fragment from any arbitrary
* id that is currently cached, unlike `writeQuery` which will only write
* from the root query.
*
* You must pass in a GraphQL document with a single fragment or a document
* with multiple fragments that represent what you are writing. If you pass
* in a document with multiple fragments then you must also specify a
* `fragmentName`.
*/
ApolloClient.prototype.writeFragment = function (options) {
var result = this.initProxy().writeFragment(options);
this.queryManager.broadcastQueries();
return result;
};
/**
* Sugar for writeQuery & writeFragment
* This method will construct a query from the data object passed in.
* If no id is supplied, writeData will write the data to the root.
* If an id is supplied, writeData will write a fragment to the object
* specified by the id in the store.
*
* Since you aren't passing in a query to check the shape of the data,
* you must pass in an object that conforms to the shape of valid GraphQL data.
*/
ApolloClient.prototype.writeData = function (options) {
var result = this.initProxy().writeData(options);
this.queryManager.broadcastQueries();
return result;
};
ApolloClient.prototype.__actionHookForDevTools = function (cb) {
this.devToolsHookCb = cb;
};
ApolloClient.prototype.__requestRaw = function (payload) {
return execute(this.link, payload);
};
/**
* This initializes the query manager that tracks queries and the cache
*/
ApolloClient.prototype.initQueryManager = function () {
var _this = this;
if (this.queryManager)
return;
this.queryManager = new QueryManager({
link: this.link,
store: this.store,
queryDeduplication: this.queryDeduplication,
ssrMode: this.ssrMode,
onBroadcast: function () {
if (_this.devToolsHookCb) {
_this.devToolsHookCb({
action: {},
state: {
queries: _this.queryManager.queryStore.getStore(),
mutations: _this.queryManager.mutationStore.getStore(),
},
dataWithOptimisticResults: _this.cache.extract(true),
});
}
},
});
};
/**
* Resets your entire store by clearing out your cache and then re-executing
* all of your active queries. This makes it so that you may guarantee that
* there is no data left in your store from a time before you called this
* method.
*
* `resetStore()` is useful when your user just logged out. You’ve removed the
* user session, and you now want to make sure that any references to data you
* might have fetched while the user session was active is gone.
*
* It is important to remember that `resetStore()` *will* refetch any active
* queries. This means that any components that might be mounted will execute
* their queries again using your network interface. If you do not want to
* re-execute any queries then you should make sure to stop watching any
* active queries.
*/
ApolloClient.prototype.resetStore = function () {
var _this = this;
return Promise.resolve()
.then(function () {
return _this.queryManager
? _this.queryManager.clearStore()
: Promise.resolve(null);
})
.then(function () { return Promise.all(_this.resetStoreCallbacks.map(function (fn) { return fn(); })); })
.then(function () {
return _this.queryManager && _this.queryManager.reFetchObservableQueries
? _this.queryManager.reFetchObservableQueries()
: Promise.resolve(null);
});
};
/**
* Allows callbacks to be registered that are executed with the store is reset.
* onResetStore returns an unsubscribe function for removing your registered callbacks.
*/
ApolloClient.prototype.onResetStore = function (cb) {
var _this = this;
this.resetStoreCallbacks.push(cb);
return function () {
_this.resetStoreCallbacks = _this.resetStoreCallbacks.filter(function (c) { return c !== cb; });
};
};
/**
* Refetches all of your active queries.
*
* `reFetchObservableQueries()` is useful if you want to bring the client back to proper state in case of a network outage
*
* It is important to remember that `reFetchObservableQueries()` *will* refetch any active
* queries. This means that any components that might be mounted will execute
* their queries again using your network interface. If you do not want to
* re-execute any queries then you should make sure to stop watching any
* active queries.
* Takes optional parameter `includeStandby` which will include queries in standby-mode when refetching.
*/
ApolloClient.prototype.reFetchObservableQueries = function (includeStandby) {
return this.queryManager
? this.queryManager.reFetchObservableQueries(includeStandby)
: Promise.resolve(null);
};
/**
* Exposes the cache's complete state, in a serializable format for later restoration.
*/
ApolloClient.prototype.extract = function (optimistic) {
return this.initProxy().extract(optimistic);
};
/**
* Replaces existing state in the cache (if any) with the values expressed by
* `serializedState`.
*
* Called when hydrating a cache (server side rendering, or offline storage),
* and also (potentially) during hot reloads.
*/
ApolloClient.prototype.restore = function (serializedState) {
return this.initProxy().restore(serializedState);
};
/**
* Initializes a data proxy for this client instance if one does not already
* exist and returns either a previously initialized proxy instance or the
* newly initialized instance.
*/
ApolloClient.prototype.initProxy = function () {
if (!this.proxy) {
this.initQueryManager();
this.proxy = this.cache;
}
return this.proxy;
};
return ApolloClient;
}());
export default ApolloClient;
//# sourceMappingURL=ApolloClient.js.map