Search code examples
apolloreact-apolloapollo-clientapollo-serverapollostack

Apollo Link State Default Resolver Not Working (@client query parameter variables)


Example here: https://codesandbox.io/s/j4mo8qpmrw

Docs here: https://www.apollographql.com/docs/link/links/state.html#default

TLDR: This is a todo list, the @client query parameters don't filter out the list.


This is the query, taking in $id as a parameter

  const GET_TODOS = gql`
    query todos($id: Int!) {
      todos(id: $id) @client {
        id
        text
      }
    }
  `;

The query passes the variable in there

<Query query={GET_TODOS} variables={{ id: 1 }}>

  /* Code */

</Query>

But the default resolver doesn't use the parameter, you can see it in the codesandbox.io example above.

The docs say it should work, but I can't seem to figure what I'm missing. Thanks in advance!


Solution

  • For simple use cases, you can often rely on the default resolver to fetch the data you need. However, to implement something like filtering the data in the cache or manipulating it (like you do with mutations), you'll need to write your own resolver. To accomplish what you're trying to do, you could do something like this:

    export const resolvers = {
      Query: {
        todos: (obj, args, ctx) => {
          const query = gql`
              query GetTodos {
                todos @client {
                  id
                  text
                }
              }
            `
          const { todos } = ctx.cache.readQuery({ query })
          return todos.filter(todo => todo.id === args.id)
        },
      },
      Mutation: {},
    }
    

    EDIT: Every Type we define has a set of fields. When we return a particular Type (or List of Types), each field on that type will utilize the default resolver to try to resolve its own value (assuming that field was requested). The way the default resolver works is simple -- it looks at the parent (or "root") object's value and if it finds a property matching the field name, it returns the value of that property. If the property isn't found (or can't be coerced into whatever Scalar or Type the field is expecting) it returns null.

    That means we can, for example, return an object representing a single Todo and we don't have to define a resolver for its id or text fields, as long as the object has id and text properties on it. Looking at it another way, if we wanted to create an arbitrary field on Todo called textWithFoo, we could leave the cache defaults as is, and create a resolver like

    (obj, args, ctx) => obj.text + ' and FOO!'
    

    In this case, a default resolver would do us no good because the objects stored in the cache don't have a textWithFoo property, so we write our own resolver.

    What's important to keep in mind is that a query like todos is just a field too (in this case, it's a field on the Query Type). It behaves pretty much the same way any other field does (including the default resolver behavior). With apollo-link-state, though, the data structure you define under defaults becomes the parent or "root" value for your queries.

    In your sample code, your defaults include a single property (todos). Because that's a property on the root object, we can fetch it with a query called todos and still get back the data even without a resolver. The default resolver for the todos field will look in the root object (in this case your cache), see a property called todos and return that.

    On the flip side, a query like todo (singular) doesn't have a matching property in the root (cache). You need to write a resolver for it to have it return data. Similarly, if you want to manipulate the data before returning it in the query (with or without arguments), you need to include a resolver.