How should I query and match data from the same response in GraphQL with Apollo Client and Link Rest?

1.3k Views Asked by At

I have the following query:

const getPage = gql`
    query Page($path: String!) {
        page(path: $path) @rest(type: "Page", path: "{args.path}") {
            blocks @type(name: Block) {
                name
                posts @type(name: Post) {
                    body
                    author
                }
            }
            authors @type(name: Author) {
                name
            }
        }
    }

In blocks.posts.author there's only an AuthorId. The authors object is containing all the available authors.

I'd like to replace/match the AuthorId with it's corresponding object. Is it possible to do this within one query?

I also wouldn't mind to have a separate query for Author only (fetch will be cached, no new request would be made), but I still don't know how would I match it through 2 queries.

Example API response

{
   blocks: [
      {
         posts: [
             {
                id: 1,
                title: 'My post',
                author: 12,
             }
         ]
      }
   ],
   authors: [
      {
         id: 12,
         name: 'John Doe'
      }
   ]
}

What I want with 1 query that author inside a post becomes the full author object.

2

There are 2 best solutions below

4
Jason Paulos On

Great question. With GraphQL, you have the power to expand any field and select the exact subfields you want from it, so if you were using GraphQL on your backend as well this would be a non-issue. There are some workarounds you can do here:

If all of the Author objects are in your Apollo cache and you have access to each Author's id, you could use ApolloClient.readFragment to access other properties, like this:

const authorId = ...; // the id of the author

const authorInfo = client.readFragment({
  id: authorId,
  fragment: gql`
    fragment AuthorInfo on Author {
      id
      name
      # anything else you want here
    }
  `,
});

Although it's worth noting that with your original query in the question, if you have all of the Author objects as a property of the query, you could just use Javascript operations to go from Author id to object.

const authorId = ...; // the id of the author
data.page.authors.find(author => author.id === authorId);
0
Daniel Rearden On

The following should work.

First, capture the author id as a variable using the @export directive. Then add a new field with some name other than author and decorate it with the @rest, using the exported variable inside the path.

So the query would look something like this:

query Page($path: String!) {
    page(path: $path) @rest(type: "Page", path: "{args.path}") {
        blocks @type(name: Block) {
            name
            posts @type(name: Post) {
                body
                author @export(as: "authorId")
                authorFull @rest(
                    path: '/authors/{exportVariables.authorId}'
                    type: 'Author'
                ) {
                    name
                }
            }
        }
        authors @type(name: Author) {
            name
        }
    }
}

You can use the fieldNameNormalizer option to rename the author property in the response to a field with a different name (for example, authorId). Ideally, that should still work with the above so you can avoid having a weird field name like authorFull but apollo-link-rest is a bit wonky so no promises.