or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cache-management.mdcache-storage.mdfragment-matching.mdindex.mdoptimistic-updates.mdstore-operations.md
tile.json

store-operations.mddocs/

Store Operations

Low-level read and write operations for direct cache manipulation and GraphQL query processing.

Capabilities

StoreReader

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
});

StoreWriter

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
});

Store Reader Configuration

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;
}

Query Read Options

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;
}

Write Context and Error Handling

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);
  }
}

Execution Results and Missing Field Handling

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
  }
}

Advanced Usage Patterns

Custom Store Operations

// 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;
  }
}

Batch Write Operations

// 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
      });
    });
  });
};

Types

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;
}