Graphql Inner and Left Join

167 views Asked by At

We are using Apollo Router + Netflix DGS to build federated graphql services. Suppose that we have a common relational database right now for books and authors, but want to move towards a microservice architecture, where books and authors are stored separately in their own database.

Suppose that I have the following graphql schema:

type Author {
  name: String
  books: [Book]
}

type Book {
  title: String!
}

type Query {
  getAuthors(name: String!, title: String) {
    authors: [Authors]
  }
}

Here are two query scenarios / results:

Scenario 1 - returns multiple authors named Lewis and list of books (0:N) associated with each author named Lewis

query {
  getAuthors("Lewis") {
    name
    books
  }
}

Scenario 2

query {
  getAuthors(name: "Lewis", title: "Cronicles of Narnia") {
    name
    books
  }
}

In a single relational database where books and authors are stored together, my query could be:

Single Relational DB Query

select * from authors a inner join books b on a.id = b.author_id where name = "Lewis" and title = "Cronicles of Narnia";

select * from authors a left join books b where a.id = b.author_id and name = "Lewis" and title = "Cronicles of Narnia";

In my future, microservice, multi-database where books and authors are stored seperately, my query could be:

Microservice / Multi-database Query:

select * from authors where name = "Lewis";

select * from books where title = "Cronicles of Narnia";

  • Then allow Apollo or Netflix DSG to join at the application level.

Question 1

What should my Scenario 2 graphql query return? I actually need to support both options below in different scenarios, so is there a different way to write the query for each?

  • Option 1) just one author named Lewis and just one book named "Cronicles of Narnia"? (this is the INNER JOIN)

  • Option 2) a list of authors named Lewis and, where linked to a specific "Lewis", append the book(s) named "Cronicles of Narnia"? (this is the LEFT JOIN)

Question 2

In my future, multi-database scenario, how would graphql (Apollo, Netflix DSG) behave? Is it more like Option 1 or Option 2? As mentioned, would need to support both.

Question 3

I've also seen a similar query written like this. Do these queries have different meanings? Is this widely supported?

query {
  getAuthors(name: "Lewis") {
    name
    books (title: "Cronicles of Narnia")
  }
}

Thanks!

Justin

1

There are 1 answers

2
Michel Floyd On

GraphQL doesn't really care/understand if you're sitting on top of a single DB or over multiple microservices - it's the job of your resolvers to map GraphQL queries into one or more db/microservice queries.

Semantically:

query {
  getAuthors("Lewis") {
    name
    books
  }
}

Should return a 400 error since you specified books but didn't include any fields from it.

query {
  getAuthors("Lewis") {
    name
    books {
      title
    }
  }
}

Should return all authors named "Lewis" and the title of every one of their books.

query {
  getAuthors(name: "Lewis", title: "Chronicles of Narnia") {
    name
    books {
      title
    }
  }
}

should return every author whose name is "Lewis" and who has written at least one book named "Chronicles of Narnia"

query {
  getAuthors(name: "Lewis") {
    name
    books (title: "Chronicles of Narnia") {
      title
    }
  }
}

should return every author named "Lewis" and for each of those the title of any book whose title is "Chronicles of Narnia". Note that in this case you won't have the opportunity to filter the authors based on the book title since the field resolver runs after the query resolver and a child can't filter its parent (Contrary to @Avius' comment)