muerwre.github.io/content/GraphQL/Apollo Client pagination.md
2022-11-04 18:29:11 +06:00

2.1 KiB

Say, we have gql response like this and we wan't to have pagination with it. Let's merge it as it specified in official documentation

query listItems(
  filter: Filter, 
  sort: String,
  limit: Number,
  offset: Number,
): ItemList!

input Filter {
  name: String!
  type: String!
}

type ItemList {
  items: [Item!]!
  totalCount: Int!
}

We will setup ApolloClient with typePolicies to merge incoming data in cache:

import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  // ...
  cache: new InMemoryCache({ typePolicies }),
  // ...
});

export const typePolicies: TypePolicies = {
  Query: {
    fields: {
      // query name
      listItems: {
        // apollo will serialize and use keyArgs as unique
        // identifier in cache for every query
        // consider choosing the right fields, 
        // i.e. limit and offset won't work here
        keyArgs: [
          'sort', // primitive type
          'filter', ['name', 'type'] // nested fields of `filter`
        ],
        merge: mergeItemsWithTotalCount,
      },
  }
}

We will need merge function mergeItemsWithTotalCount, which will join results of query and cached data for specific key:

/** merges all sources with { items: unknown[], totalCount: number } */
const mergeItemsWithTotalCount = (existing, incoming, { args }) => {
    // no existing data
    if (!existing || !args?.offset || args.offset < existing.length) {
      return incoming || [];
    }

    // If hook was called multiple times
    if (existing?.items?.length && args?.offset < existing.items.length) {
      return existing || [];
    }

    // merge cache and incoming data
    const items = [...(existing?.items || []), ...(incoming?.items || [])];

    // apply latest result for totalCount
    const totalCount = incoming?.totalCount || existing?.totalCount;

    return {
      ...(incoming || existing || {}),
      items,
      totalCount,
    };
  };