I am new to GraphQL and I cannot understand what is the correct way of resolving queries when the database fields are different from the GraphQL schema fields.
I am using Apollo Server.
My DB schema is like this one:
// db schema
type Book {
id: number
title: string
author_id: number
}
type Author {
id: number
name: string
}
My GraphQL schema must be like this one:
// gql schema
type Book {
title: String!
author: Author!
}
type Author {
name: String!
books: [Book]!
}
type Query {
author(id: ID!): Author
book(id: ID!): Book
authors: [Author]
books: [Book]
}
Now what I understood is that I have to manually translate the database object to the graphql response object with resolvers.
So these are my resolvers:
const resolvers:Resolvers = {
Book: {
author: async (parent, args, context, info) => {
const authors = await db.author.select({where: {id: parent.author_id}});
~~~~~~~~~~~~~~~~~~~~~ -> FIRST PROBLEM
return authors[0];
}
},
Author: {
books: async (parent, args, context, info) => {
const books = await db.books.select({where: {author_id: parent.id}});
return books;
}
},
Query: {
book: async (parent, args, context, info) => {
const books = await db.author.select({where: {id: args.id}});
return books[0];
~~~~~~~~~~~~~~~~ -> SECOND PROBLEM
}
author: async (parent, args, context, info) => {
const authors = await db.author.select({where: {id: args.id}});
return authors[0];
~~~~~~~~~~~~~~~~~ -> SAME AS SECOND PROBLEM
},
books: async (parent, args, context, info) => {
const books = await db.books.select();
return books;
}
authors: async (parent, args, context, info) => {
const authors = await db.authors.select();
return authors;
}
}
}
FIRST PROBLEM:
When I resolve the author
field in Book, I don't have the author_id
fields because it is not defined in the graphql schema.
Should I always add author_id
also in the GQL Schema?
SECOND PROBLEM:
How can I resolve the Author
in Query.book
? Is there a proper way to resolve it? I don't want to make another DB call, because this will lead me to an infinite loop, right?
SAME AS SECOND PROBLEM:
How can I resolve the [Book]
in Query.author
? Is there a proper way to resolve the it?
I put these problems together because I think they are all related to the same solutions somehow. I just cannot see it.
Can somebody know how to solve it?
Regarding the SECOND PROBLEM and SAME AS SECOND PROBLEMS, they are not really problems.
In those methods you can resolve every field expect for the "object type" fields, like author
for Book
and books
for Author
. You already resolved them above. The service will take care of adding them to the result.
Regarding the FIRST PROBLEM if you don't want to add author_id
you can do something like:
const resolvers:Resolvers = {
Book: {
author: async (parent, args, context, info) => {
const books = await db.book.select({where: {id: parent.id}});
const author_id = books[0].author_id;
const authors = await db.author.select({where: {id: author_id}});
return authors[0];
}
},
But with this method you are going to make 2 queries, so it might be a bit slower.
You can also resolve the Book with more attributes than what is defined in the GraphQL schema. So you can return a book object that has also the author_id
attribute. The service won't show it but the resolver can still use it. So for example:
const resolvers:Resolvers = {
Book: {
author: async (parent, args, context, info) => {
const authors = await db.author.select({where: {id:parent.author_id}});
return authors[0];
},
// ...
},
Query: {
book: async (parent, args, context, info) => {
const books = await db.author.select({where: {id: args.id}});
return books[0];
// ~~~~~~~~~~~~~~~~ -> return a book with also `author_id`.
},
// ...
}
}
The problem with this is that if you are using Typescript, it won't allow it, since it wants you return and object with the correct fields.