How to use readField in apollo client polices for sub selecting a query that has arguments

Viewed 1268

Using @apollo/client 3.4.7

I have been following this great blog by Dan Reynolds https://www.apollographql.com/blog/apollo-client/architecture/redux-to-apollo-data-access-patterns/ which explains moving from a Redux world to an Apollo GraphQL world. A nice part of Redux is the ability to use selectors to select portions of data from your state. In Apollo land you are able to run queries, but sometimes you already have your data and only need a subset of it. The blog goes into some nice detail explaining you can do something akin to:

const schema: TypePoliciesSchema = {
  typeDefs: gql`
    extend type Query {
      readManagers: [Employee!]!
    }
  `,
  typePolicies: {
    Query: {
      fields: {
        readManagers: {
          read(employees, context) {
            const employees = readField<Query, 'readEmployees'>(context, 'readEmployees');

            return employees.filter(employeeRef => {
              const employeeRole = readField<Employee, 'role'>(context, 'role', employeeRef);
              return employeeRole === EmployeeRole.Manager;
            });
          },
        }
      }
    }
  }
}

This is based on a simple GraphQL query:

query GetEmployees {
  employees {
    id
    name
    role
    team
  }
}

My question is how can you get the query results when your query has parameters:

query GetEmployees($companyId: ID!) {
  employees(companyId: $companyId) {
    id
    name
    role
    team
  }
}

Suddenly you can't get access to readEmployees because the cache key is now something like readEmployees({"companyId": "uuid"})?

I appreciate that you may have to setup the readManagers query to use params too, to get the same ID to be able to make a sub selection. I tried manually fudging the query key to match but it didn't work.

My work around at the moment is to use the cache from the context and run the same query and many convert them to references with toReference:

const schema: TypePoliciesSchema = {
  typeDefs: gql`
    extend type Query {
      readManagers: [Employee!]!
    }
  `,
  typePolicies: {
    Query: {
      fields: {
        readManagers: {
          read(employees, context) {
            const employees = context.cache.readQuery('readEmployees', { variables: { companyId: context.args.companyId || context.variables.companyId } } );
            return employees.filter(employeeRef => {
              const employeeRole = readField<Employee, 'role'>(context, 'role', employeeRef);
              return employeeRole === EmployeeRole.Manager;
            });
          },
        }
      }
    }
  }
}

This doesn't feel like the best solution, and feels like a lot of boilerplate for reading a sub selection of the cache.

1 Answers

According to the answer from github issue you can use another form of readField function which takes object. Then you can use it in your type policy definition to read data with arguments

 users: {
        read(users, { args, readField }) {
             const { where: { id } } = args;

             const users = readField({
                fieldName: 'Users',
                args: { where: { id } },
              });

             return users;
        },
   }
Related