Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add imperative read method to ApolloClient #1280

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Expect active development and potentially significant breaking changes in the `0.x` track. We'll try to be diligent about releasing a `1.0` version in a timely fashion (ideally within 3 to 6 months), to signal the start of a more stable API.

### vNEXT
- ...
- Add imperative `read` method to client for arbitrary data access from the store. [PR #1280](https://github.com/apollographql/apollo-client/pull/1280)

### 0.8.6
- Fix bug that caused `refetch` to not refetch in some cases [PR #1264](https://github.com/apollographql/apollo-client/pull/1264)
Expand Down
101 changes: 101 additions & 0 deletions src/ApolloClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
SelectionSetNode,
/* tslint:enable */

DocumentNode,
FragmentDefinitionNode,
} from 'graphql';

import {
Expand Down Expand Up @@ -58,6 +60,10 @@ import {
storeKeyNameFromFieldNameAndArgs,
} from './data/storeUtils';

import {
readQueryFromStore,
} from './data/readFromStore';

import {
version,
} from './version';
Expand Down Expand Up @@ -336,6 +342,101 @@ export default class ApolloClient {
return this.queryManager.startGraphQLSubscription(realOptions);
}

/**
* Tries to read some data from the store without making a network request.
* This method will start at the root query. To start at a a specific id in
* the store then use `readFragment`.
*/
public readQuery <QueryType>({
query,
variables,
returnPartialData,
}: {
query: DocumentNode,
variables?: Object,
returnPartialData?: boolean,
}): QueryType {
this.initStore();

const reduxRootSelector = this.reduxRootSelector || defaultReduxRootSelector;

return readQueryFromStore<QueryType>({
store: reduxRootSelector(this.store.getState()).data,
query,
variables,
returnPartialData,
});
}

/**
* Tries to read some data from the store without making a network request.
* This method will read a GraphQL fragment from any arbitrary id in the
* store. Unlike `readQuery` which will only read from the root query.
*/
public readFragment <FragmentType>({
id,
fragment,
fragmentName,
variables,
returnPartialData,
}: {
id: string,
fragment: DocumentNode,
fragmentName?: string,
variables?: Object,
returnPartialData?: boolean,
}): FragmentType {
this.initStore();

const reduxRootSelector = this.reduxRootSelector || defaultReduxRootSelector;
let actualFragmentName = fragmentName;

// If the user did not give us a fragment name then let us try to get a
// name from a single fragment in the definition.
if (typeof actualFragmentName === 'undefined') {
const fragments = fragment.definitions.filter(({ kind }) => kind === 'FragmentDefinition') as Array<FragmentDefinitionNode>;
if (fragments.length !== 1) {
throw new Error(`Found ${fragments.length} fragments when exactly 1 was expected because \`fragmentName\` was not provided.`);
}
actualFragmentName = fragments[0].name.value;
}

// Generate a query document with an operation that simply spreads the
// fragment inside of it. This is necessary to be compatible with our
// current store implementation, but we want users to write fragments to
// support GraphQL tooling everywhere.
const query: DocumentNode = {
...fragment,
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: {
kind: 'Name',
value: actualFragmentName,
},
},
],
},
},
...fragment.definitions,
],
};

return readQueryFromStore<FragmentType>({
rootId: id,
store: reduxRootSelector(this.store.getState()).data,
query,
variables,
returnPartialData,
});
}

/**
* Returns a reducer function configured according to the `reducerConfig` instance variable.
*/
Expand Down
4 changes: 3 additions & 1 deletion src/data/readFromStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type ReadQueryOptions = {
variables?: Object,
returnPartialData?: boolean,
previousResult?: any,
rootId?: string,
config?: ApolloReducerConfig,
};

Expand Down Expand Up @@ -247,6 +248,7 @@ export function diffQueryAgainstStore({
variables,
returnPartialData = true,
previousResult,
rootId = 'ROOT_QUERY',
config,
}: ReadQueryOptions): DiffResult {
// Throw the right validation error by trying to find a query in the document
Expand All @@ -264,7 +266,7 @@ export function diffQueryAgainstStore({

const rootIdValue = {
type: 'id',
id: 'ROOT_QUERY',
id: rootId,
previousResult,
};

Expand Down
Loading