import { NetworkInterface, createNetworkInterface, } from './transport/networkInterface'; import { // We need to import this here to allow TypeScript to include it in the definition file even // though we don't use it. https://github.com/Microsoft/TypeScript/issues/5711 // We need to disable the linter here because TSLint rightfully complains that this is unused. /* tslint:disable */ SelectionSetNode, /* tslint:enable */ DocumentNode, FragmentDefinitionNode, } from 'graphql'; import { HeuristicFragmentMatcher, FragmentMatcherInterface, } from './data/fragmentMatcher'; import { createApolloStore, ApolloStore, createApolloReducer, ApolloReducerConfig, Store, } from './store'; import { ApolloAction, } from './actions'; import { CustomResolverMap, } from './data/readFromStore'; import { QueryManager, } from './core/QueryManager'; import { ApolloQueryResult, IdGetter, } from './core/types'; import { ObservableQuery, } from './core/ObservableQuery'; import { Observable, } from './util/Observable'; import { isProduction, } from './util/environment'; import { WatchQueryOptions, SubscriptionOptions, MutationOptions, } from './core/watchQueryOptions'; import { storeKeyNameFromFieldNameAndArgs, } from './data/storeUtils'; import { getFragmentQueryDocument, } from './queries/getFromAST'; import { DataProxy, DataProxyReadQueryOptions, DataProxyReadFragmentOptions, DataProxyWriteQueryOptions, DataProxyWriteFragmentOptions, ReduxDataProxy, } from './data/proxy'; import { version, } from './version'; /** * This type defines a "selector" function that receives state from the Redux store * and returns the part of it that is managed by ApolloClient * @param state State of a Redux store * @returns {Store} Part of state managed by ApolloClient */ export type ApolloStateSelector = (state: any) => Store; const DEFAULT_REDUX_ROOT_KEY = 'apollo'; function defaultReduxRootSelector(state: any) { return state[DEFAULT_REDUX_ROOT_KEY]; } function defaultDataIdFromObject (result: any): string | null { if (result.__typename) { if (result.id !== undefined) { return `${result.__typename}:${result.id}`; } if (result._id !== undefined) { return `${result.__typename}:${result._id}`; } } return null; } let hasSuggestedDevtools = false; /** * 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 Redux store. It also delivers updates * to GraphQL queries through {@link Observable} instances. */ export default class ApolloClient implements DataProxy { public networkInterface: NetworkInterface; public store: ApolloStore; public reduxRootSelector: ApolloStateSelector | null; public initialState: any; public queryManager: QueryManager; public reducerConfig: ApolloReducerConfig; public addTypename: boolean; public disableNetworkFetches: boolean; public dataId: IdGetter | undefined; public fieldWithArgs: (fieldName: string, args?: Object) => string; public version: string; public queryDeduplication: boolean; private devToolsHookCb: Function; private proxy: DataProxy | undefined; private fragmentMatcher: FragmentMatcherInterface; /** * Constructs an instance of {@link ApolloClient}. * * @param networkInterface The {@link NetworkInterface} over which GraphQL documents will be sent * to a GraphQL spec-compliant server. * * @param reduxRootSelector A "selector" function that receives state from the Redux store * and returns the part of it that is managed by ApolloClient. * This option should only be used if the store is created outside of the client. * * @param initialState The initial state assigned to the store. * * @param dataIdFromObject A function that returns a object identifier given a particular result * object. * * @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 addTypename Adds the __typename field to every level of a GraphQL document, required * to support certain queries that contain fragments. * * @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. * * @param fragmentMatcher A function to use for matching fragment conditions in GraphQL documents */ constructor(options: { networkInterface?: NetworkInterface, reduxRootSelector?: string | ApolloStateSelector, initialState?: any, dataIdFromObject?: IdGetter, ssrMode?: boolean, ssrForceFetchDelay?: number addTypename?: boolean, customResolvers?: CustomResolverMap, connectToDevTools?: boolean, queryDeduplication?: boolean, fragmentMatcher?: FragmentMatcherInterface, } = {}) { let { dataIdFromObject, } = options; const { networkInterface, reduxRootSelector, initialState, ssrMode = false, ssrForceFetchDelay = 0, addTypename = true, customResolvers, connectToDevTools, fragmentMatcher, queryDeduplication = true, } = options; if (typeof reduxRootSelector === 'function') { this.reduxRootSelector = reduxRootSelector; } else if (typeof reduxRootSelector !== 'undefined') { throw new Error('"reduxRootSelector" must be a function.'); } if (typeof fragmentMatcher === 'undefined') { this.fragmentMatcher = new HeuristicFragmentMatcher(); } else { this.fragmentMatcher = fragmentMatcher; } this.initialState = initialState ? initialState : {}; this.networkInterface = networkInterface ? networkInterface : createNetworkInterface({ uri: '/graphql' }); this.addTypename = addTypename; this.disableNetworkFetches = ssrMode || ssrForceFetchDelay > 0; this.dataId = dataIdFromObject = dataIdFromObject || defaultDataIdFromObject; this.fieldWithArgs = storeKeyNameFromFieldNameAndArgs; this.queryDeduplication = queryDeduplication; if (ssrForceFetchDelay) { setTimeout(() => this.disableNetworkFetches = false, ssrForceFetchDelay); } this.reducerConfig = { dataIdFromObject, customResolvers, addTypename, fragmentMatcher: this.fragmentMatcher.match, }; this.watchQuery = this.watchQuery.bind(this); this.query = this.query.bind(this); this.mutate = this.mutate.bind(this); this.setStore = this.setStore.bind(this); this.resetStore = this.resetStore.bind(this); // Attach the client instance to window to let us be found by chrome devtools, but only in // development mode const defaultConnectToDevTools = !isProduction() && typeof window !== 'undefined' && (!(window as any).__APOLLO_CLIENT__); if (typeof connectToDevTools === 'undefined' ? defaultConnectToDevTools : connectToDevTools) { (window as any).__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 as any).__APOLLO_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { // Only for Chrome if (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. *
* 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. * * 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. * * See [here](https://medium.com/apollo-stack/the-concepts-of-graphql-bc68bd819be3#.3mb0cbcmc) for * a description of store reactivity. * */ public watchQuery