Lesson 1
Nested Resolvers and Data Relationships
Introduction

GraphQL enables efficient and flexible data querying, reducing payload size and improving application performance.

In this lesson, we use Apollo Server to explore nested resolvers and data relationships in GraphQL. By the end, you should be able to create a GraphQL schema with nested types and use nested resolvers to handle complex data relationships.

GraphQL Schemas and Types

A GraphQL schema defines the structure of the API and the types of data it can return.

Here’s an example defining two types, Author and Book, which have a nested relationship:

TypeScript
1const typeDefs = gql` 2 type Author { 3 id: ID! 4 name: String! 5 books: [Book] 6 } 7 8 type Book { 9 id: ID! 10 title: String! 11 author: Author 12 } 13 14 type Query { 15 books: [Book] 16 authors: [Author] 17 } 18`;
  • Author has an id, name, and a list of books.
  • Book has an id, title, and an author.
  • Query fetches lists of both books and authors.
Building and Understanding Resolvers

Resolvers fetch the data for fields defined in the schema. We use nested resolvers for nested data.

Here’s an example:

TypeScript
1const resolvers = { 2 Query: { 3 books: () => books, 4 authors: () => authors, 5 }, 6 Author: { 7 books: (author) => books.filter(book => book.author.id === author.id), 8 }, 9 Book: { 10 author: (book) => authors.find(author => author.id === book.author.id), 11 }, 12};

In the example above:

  • The Query resolver returns the full lists of books and authors.
  • The Author resolver fetches books associated with an author.
  • The Book resolver retrieves the author associated with a book.

The nested resolvers work seamlessly because GraphQL calls the resolvers depth-first. For example, when querying a book's author, it first resolves the book and then the associated author field. This also prevents infinite loops because the nested calls are handled recursively within the resolution lifecycle.

Example Implementation

Given the previously defined schema and resolvers, let's define our sample data:

TypeScript
1const authors = [ 2 { id: '1', name: 'J.R.R. Tolkien' }, 3 { id: '2', name: 'J.K. Rowling' } 4]; 5 6const books = [ 7 { id: '1', title: 'The Hobbit', author: authors[0] }, 8 { id: '2', title: 'Harry Potter', author: authors[1] } 9];

And initialize and start the Apollo Server:

TypeScript
1const server = new ApolloServer({ typeDefs, resolvers }); 2server.listen().then(({ url }) => { 3 console.log(`🚀 Server ready at ${url}`); 4});

When you run your server.ts file, it should print:

Plain text
1🚀 Server ready at http://localhost:4000/
Executing Queries

Now that your GraphQL server is up and running, let's execute a query to fetch nested data.

Example Query:

TypeScript
1const query = ` 2 query { 3 books { 4 title 5 author { 6 name 7 } 8 } 9 authors { 10 name 11 books { 12 title 13 } 14 } 15 } 16`;

Fetching Data:

TypeScript
1import fetch from 'node-fetch'; 2 3const query = ` 4 query { 5 books { 6 title 7 author { 8 name 9 } 10 } 11 authors { 12 name 13 books { 14 title 15 } 16 } 17 } 18`; 19 20const url = 'http://localhost:4000/'; 21 22fetch(url, { 23 method: 'POST', 24 headers: { 25 'Content-Type': 'application/json', 26 }, 27 body: JSON.stringify({ query }), 28}) 29 .then((response) => response.json()) 30 .then((data) => console.log(JSON.stringify(data, null, 2))) 31 .catch((error) => console.error('Error:', error));

Executing this script fetches the nested data relationships and logs them. You should see an output similar to:

JSON
1{ 2 "data": { 3 "books": [ 4 { 5 "title": "The Hobbit", 6 "author": { 7 "name": "J.R.R. Tolkien" 8 } 9 }, 10 { 11 "title": "Harry Potter", 12 "author": { 13 "name": "J.K. Rowling" 14 } 15 } 16 ], 17 "authors": [ 18 { 19 "name": "J.R.R. Tolkien", 20 "books": [ 21 { 22 "title": "The Hobbit" 23 } 24 ] 25 }, 26 { 27 "name": "J.K. Rowling", 28 "books": [ 29 { 30 "title": "Harry Potter" 31 } 32 ] 33 } 34 ] 35 } 36}
Lesson Summary

In this lesson, we covered how to define GraphQL schemas with nested types and implement nested resolvers for complex data relationships. We also demonstrated executing queries to fetch nested data.

As you move on to the practice exercises, try creating your own schemas and resolvers. Practice is essential to solidify your understanding and improve your skills. This will set a strong foundation for tackling more advanced topics in GraphQL.

Good luck, and enjoy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.