Low-level read and write operations for direct cache manipulation and GraphQL query processing.
Handles reading data from the normalized cache store and processing GraphQL queries against cached data.
/**
* Reads data from normalized cache store
* Processes GraphQL queries against cached data with fragment support
*/
class StoreReader {
/**
* Creates store reader with configuration
* @param config - Reader configuration including cache keys and result freezing
*/
constructor(config: StoreReaderConfig);
/**
* Reads complete query from store
* @param options - Query options including document, variables, and context
* @returns Query result data or null if incomplete
*/
readQueryFromStore<QueryType>(options: ReadQueryOptions): QueryType;
/**
* Compares query against store to detect changes
* @param options - Diff options including query, variables, and previous result
* @returns Diff result indicating completeness and changes
*/
diffQueryAgainstStore<T>(options: DiffQueryAgainstStoreOptions): Cache.DiffResult<T>;
}Usage Example:
import { StoreReader, DepTrackingCache } from "apollo-cache-inmemory";
import { gql } from "graphql-tag";
const cache = new DepTrackingCache();
const reader = new StoreReader({
cacheKeyRoot: new KeyTrie(),
freezeResults: false
});
// Read query from store
const query = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
const result = reader.readQueryFromStore({
store: cache,
query: query,
variables: { id: "1" },
fragmentMatcherFunction: fragmentMatcher.match,
config: apolloConfig
});
// Diff query against store
const diffResult = reader.diffQueryAgainstStore({
store: cache,
query: query,
variables: { id: "1" },
returnPartialData: true,
previousResult: previousData,
fragmentMatcherFunction: fragmentMatcher.match,
config: apolloConfig
});Handles writing data to the normalized cache store from GraphQL query results.
/**
* Writes data to normalized cache store
* Processes GraphQL mutation results and query data for storage
*/
class StoreWriter {
/**
* Writes query result to store
* @param options - Write options including query, result, and normalization config
* @returns Updated normalized cache
*/
writeQueryToStore(options: {
query: DocumentNode;
result: Object;
store?: NormalizedCache;
variables?: Object;
dataIdFromObject?: IdGetter;
fragmentMatcherFunction?: FragmentMatcher;
}): NormalizedCache;
/**
* Writes result data to store with specific root ID
* @param options - Write options including data ID, result, and context
*/
writeResultToStore(options: {
dataId: string;
result: any;
variables?: Object;
document: DocumentNode;
store: NormalizedCache;
dataIdFromObject?: IdGetter;
fragmentMatcherFunction?: FragmentMatcher;
}): void;
}Usage Example:
import { StoreWriter, ObjectCache } from "apollo-cache-inmemory";
import { gql } from "graphql-tag";
const writer = new StoreWriter();
const cache = new ObjectCache();
// Write mutation result to store
const mutation = gql`
mutation UpdateUser($id: ID!, $name: String!) {
updateUser(id: $id, name: $name) {
id
name
updatedAt
}
}
`;
const mutationResult = {
updateUser: {
id: "1",
name: "Alice Updated",
updatedAt: "2023-01-01T12:00:00Z",
__typename: "User"
}
};
writer.writeResultToStore({
dataId: "ROOT_MUTATION",
result: mutationResult,
variables: { id: "1", name: "Alice Updated" },
document: mutation,
store: cache,
dataIdFromObject: defaultDataIdFromObject,
fragmentMatcherFunction: fragmentMatcher.match
});
// Write query result to store
const queryResult = {
user: {
id: "1",
name: "Alice",
email: "alice@example.com",
__typename: "User"
}
};
const updatedCache = writer.writeQueryToStore({
query: GET_USER_QUERY,
result: queryResult,
store: cache,
variables: { id: "1" },
dataIdFromObject: defaultDataIdFromObject
});Configuration interface for customizing StoreReader behavior.
interface StoreReaderConfig {
/**
* Cache key trie for memoization and dependency tracking
* Used for optimizing repeated query reads
*/
cacheKeyRoot?: KeyTrie<object>;
/**
* Whether to freeze result objects to prevent mutation
* Helps catch accidental mutations but impacts performance
*/
freezeResults?: boolean;
}Options for reading queries from the store.
interface ReadQueryOptions {
/** Cache store to read from */
store: NormalizedCache;
/** GraphQL query document */
query: DocumentNode;
/** Function for matching fragments on unions/interfaces */
fragmentMatcherFunction?: FragmentMatcher;
/** Query variables */
variables?: Object;
/** Previous result for comparison */
previousResult?: any;
/** Root ID to start reading from (default: ROOT_QUERY) */
rootId?: string;
/** Apollo cache configuration */
config?: ApolloReducerConfig;
}
interface DiffQueryAgainstStoreOptions extends ReadQueryOptions {
/** Whether to return partial data when some fields are missing */
returnPartialData?: boolean;
}Context and error types for store write operations.
interface WriteContext {
/** Cache store being written to */
readonly store: NormalizedCache;
/** Processed field data for deduplication */
readonly processedData?: { [x: string]: FieldNode[] };
/** GraphQL variables for the operation */
readonly variables?: any;
/** Function for generating object IDs */
readonly dataIdFromObject?: IdGetter;
/** Fragment map for resolving fragment spreads */
readonly fragmentMap?: FragmentMap;
/** Function for matching fragments */
readonly fragmentMatcherFunction?: FragmentMatcher;
}
/**
* Error class for store write operations
* Thrown when writes fail due to invalid data or configuration
*/
class WriteError extends Error {
public type = 'WriteError';
}
/**
* Enhances error with GraphQL document information
* @param error - Original error to enhance
* @param document - GraphQL document that caused the error
* @returns Enhanced error with document context
*/
function enhanceErrorWithDocument(error: Error, document: DocumentNode): WriteError;Usage Example:
import { StoreWriter, enhanceErrorWithDocument, WriteError } from "apollo-cache-inmemory";
const writer = new StoreWriter();
try {
writer.writeResultToStore({
dataId: "ROOT_QUERY",
result: queryResult,
document: query,
store: cache,
dataIdFromObject: customIdGenerator,
fragmentMatcherFunction: fragmentMatcher.match
});
} catch (error) {
if (error instanceof WriteError) {
console.error("Store write failed:", error.message);
} else {
// Enhance generic errors with document context
const enhancedError = enhanceErrorWithDocument(error, query);
console.error("Store operation failed:", enhancedError.message);
}
}Types for handling query execution results and missing data.
interface ExecResult<R = any> {
/** Query execution result data */
result: R;
/** Array of missing fields encountered during execution */
missing?: ExecResultMissingField[];
}
interface ExecResultMissingField {
/** Object that was missing the field */
object: StoreObject;
/** Name of the missing field */
fieldName: string;
/** Whether missing field is tolerable (doesn't break query) */
tolerable: boolean;
}
/**
* Validates IdValue objects
* @param idValue - IdValue to validate
* @throws Error if IdValue is invalid
*/
function assertIdValue(idValue: IdValue): void;Usage Example:
// Handle missing fields in query results
const result = reader.readQueryFromStore({
store: cache,
query: query,
variables: variables,
returnPartialData: true
});
if (result.missing && result.missing.length > 0) {
const tolerableMissing = result.missing.filter(field => field.tolerable);
const criticalMissing = result.missing.filter(field => !field.tolerable);
if (criticalMissing.length > 0) {
console.warn("Critical fields missing:", criticalMissing);
// Trigger refetch or show loading state
}
if (tolerableMissing.length > 0) {
console.info("Optional fields missing:", tolerableMissing);
// Continue with partial data
}
}// Custom store reader with advanced caching
class CustomStoreReader extends StoreReader {
constructor(config: StoreReaderConfig) {
super({
...config,
cacheKeyRoot: new KeyTrie(true), // Enable weak map caching
freezeResults: process.env.NODE_ENV === 'development'
});
}
// Override for custom result processing
readQueryFromStore<T>(options: ReadQueryOptions): T {
const result = super.readQueryFromStore(options);
// Add custom post-processing
return this.postProcessResult(result);
}
private postProcessResult<T>(result: T): T {
// Custom result transformation
return result;
}
}// Efficient batch writing
const performBatchWrite = (operations: WriteOperation[]) => {
const writer = new StoreWriter();
// Group operations by store for efficiency
const operationsByStore = new Map<NormalizedCache, WriteOperation[]>();
operations.forEach(op => {
if (!operationsByStore.has(op.store)) {
operationsByStore.set(op.store, []);
}
operationsByStore.get(op.store)!.push(op);
});
// Execute grouped operations
operationsByStore.forEach((ops, store) => {
ops.forEach(op => {
writer.writeResultToStore({
dataId: op.dataId,
result: op.result,
document: op.document,
store: store,
variables: op.variables,
dataIdFromObject: op.dataIdFromObject,
fragmentMatcherFunction: op.fragmentMatcherFunction
});
});
});
};type FragmentMatcher = (
rootValue: any,
typeCondition: string,
context: ReadStoreContext
) => boolean | 'heuristic';
type VariableMap = { [name: string]: any };
interface IdValue {
type: "id";
id: string;
generated: boolean;
}
interface ReadStoreContext {
readonly store: NormalizedCache;
readonly cacheRedirects: CacheResolverMap;
readonly dataIdFromObject?: IdGetter;
}
type IdGetter = (value: IdGetterObj) => string | null | undefined;
interface IdGetterObj extends Object {
__typename?: string;
id?: string;
}