Skip to content
This repository has been archived by the owner on Jul 10, 2019. It is now read-only.

Resolvers should look for a custom resolver first, but this would break the mixed @client / server query #226

Open
PCreations opened this issue Mar 21, 2018 · 7 comments
Assignees
Labels
ac-local-state-ready blocking Prevents production or dev due to perf, bug, build error, etc.. enhancement

Comments

@PCreations
Copy link

PCreations commented Mar 21, 2018

When resolving a field, the resolve function should first look for a custom resolver, thus simulating the same behavior as on a server :

const query = gql`
query {
  messages {
    content
    user {
      username
    }
  }
}
`;
const resolvers = {
  Message: {
    user: ({ user }) => ({
      ...db.getUserById(user),
      __typename: 'User',
    }),
  },
  Query: {
    messages: () => [{ id: "m1", content: "message 1", user: "u1",  __typename: "Message" }]
  }
}

With apollo-link-state, due to this statement stating that if there is a field in the root value, it's because the server already sent something. It's not true, this statement should be valid only if we are at the Query level.

Here is a failing test :

it('runs resolvers for nested client queries', done => {
  const nestedQuery = gql`
    query NestedQuery {
      foo @client {
        bar
        nestedBar {
          baz
        }
      }
    }
  `;
  const getNestedBarById = id => id === '42' ? {
    baz: true,
  } : null;

  const client = withClientState({
    resolvers: {
      Foo: {
        nestedBar: ({ nestedBar }) => getNestedBarById(nestedBar),
      },
      Query: {
        foo: () => ({ bar: true, nestedBar: '42', __typename: 'Foo' }),
      },
    }
  });
  execute(client, { query: nestedQuery }).subscribe(({ data }) => {
    expect(data).toEqual({ foo: { bar: true, nestedBar: { baz: true } } });
    done();
  }, done.fail);
});
// TypeError: Cannot read property 'baz' of undefined

Quick edit (to be refactored) to make all the tests pass (this one included) :
in index.js

const resolver = (fieldName, rootValue = {}, args, context, info) => {

  //resultKey is where data under the field name is ultimately returned by the server
  //https://github.com/apollographql/apollo-client/tree/master/packages/graphql-anywhere#resolver-info
  const fieldValue = rootValue[info.resultKey];

  //If fieldValue is defined and we are at the Query level, server returned a value
  if (fieldValue !== undefined && (((rootValue as any).__typename || type) == 'Query')) {
    return fieldValue;
  }

  // Look for the field in the custom resolver map
  const resolverMap = resolvers[(rootValue as any).__typename || type];
  if (resolverMap) {
    const resolve = resolverMap[fieldName];
    if (resolve) return resolve(rootValue, args, context, info);
    if (fieldValue !== undefined) return fieldValue;
  }
  if (fieldValue !== undefined) {
    return fieldValue;
  }

  //TODO: the proper thing to do here is throw an error saying to
  //add `client.onResetStore(link.writeDefaults);`
  //waiting on https://github.com/apollographql/apollo-client/pull/3010
  //Currently with nested fields, this sort of return does not work

  return defaults[fieldName];
};
@PCreations PCreations changed the title Warn users about the impossibility to apply Value Completion as in the graphql specs due to the lack of client-side schema Resolvers should look for a custom resolver first, but this would break the mixed @client / server query Mar 22, 2018
@peggyrayzis
Copy link
Contributor

Hi @PCreations, can you please send in a PR with the failing test and fix? It's impossible for me to see the file diff otherwise. Thanks!

@PCreations
Copy link
Author

Sure thing ! Here we go : #235

@fbartho fbartho added the blocking Prevents production or dev due to perf, bug, build error, etc.. label Apr 8, 2018
JeremyJonas added a commit to JeremyJonas/apollo-link-state that referenced this issue May 9, 2018
@MikaelCarpenter
Copy link

any chance someone could explain the "this would break the mixed @client / server query" bit? My team is running into this issue and are looking to help with a fix.

@fbartho
Copy link
Contributor

fbartho commented Jul 18, 2018

Related: #272

@marktani
Copy link

Still running into this issue, anyone looking into this or pointers for a fix? Happy to take a stab at this myself.

@alexanderchan
Copy link

@marktani I just tested on Apollo client 2.1 and this seems to be, for lack of a better word, resolved. If you can migrate then it will probably solve the problem, complete with async resolution:

Example client side schema

const typeDefs = gql`
  extend type Query {
    getTasks: [Task]
    isLoggedIn: Boolean
  }

  type Task {
    id: String
    comments: [Comment]
  }

  type Comment {
    id: String
    commentText: String
  }
`;

For example, defining this as the resolver:

const client = new ApolloClient({
  cache,
  resolvers: {
    Query: {
      getTasks: (root, variables, { cache, getCacheKey }) => {
        return [
          {
            __typename: "Task",
            id: "test"
          }
        ];
      }
    },

    // Help to resolve the dependencies
    Task: {
      comments: async () => {
        return [
          {
            __typename: "Comment",
            id: "commentId",
            commentText: "the work text"
          },
          {
            __typename: "Comment",
            id: "commentId2",
            commentText: "another comment"
          }
        ];
      }
    }
  },
  typeDefs
});

const GET_TASKS = gql`
  query {
    getTasks @client {
      id
      comments {
        id
        commentText
      }
    }
  }
`;

@lorenjohnson
Copy link

lorenjohnson commented Jun 3, 2019

I am experiencing this issue currently when nesting more than one level. I will try and get around to a complete code example soon. For now I'm looking for workarounds.

Here is the quick and dirty:

This works, resolving both community and child posts data using a posts resolver within the Community resolver object :

query PostsQuery {
  community(slug: "test") @client {
    id
    name
    posts @client {
      id
      title
    }
  }
}

This query breaks complaining that there is no comments field found in the results despite that I do have a comments resolver on the Post resolver object:

query PostsWithCommentsQuery {
  community(slug: "test") @client {
    id
    name
    posts @client {
      id
      title
      comments @client {
        id
        text
      }
    }
  }
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
ac-local-state-ready blocking Prevents production or dev due to perf, bug, build error, etc.. enhancement
Projects
None yet
Development

No branches or pull requests

8 participants