In this lesson, we will build upon your existing GraphQL skills by introducing advanced query and mutation arguments. These techniques will enable you to create more flexible and powerful APIs. Advanced arguments allow for better precision in the data you request and the operations you perform.
Let's start by defining our GraphQL schema. The schema is a blueprint for the structure of your API.
Below is the schema we will use:
TypeScript1const typeDefs = gql` 2 type Book { 3 id: ID! 4 title: String! 5 author: String! 6 publishedDate: String 7 genre: String 8 } 9 10 type Query { 11 books(genre: String, author: String): [Book] 12 } 13 14 type Mutation { 15 addBook(title: String!, author: String!, publishedDate: String, genre: String): Book 16 } 17`;
In this schema:
Book
type defines the structure of a book object.Query
type has abooks
field that accepts two optional arguments,genre
andauthor
, to filter books.Mutation
type has anaddBook
field that accepts arguments to add a new book to our dataset.
Resolvers fetch the data specified in the schema. Here, we will write resolvers to handle the books
query with filtering capabilities:
TypeScript1const resolvers = { 2 Query: { 3 books: (_: unknown, { genre, author }: { genre?: string; author?: string }) => { 4 return books.filter(book => 5 (genre ? book.genre === genre : true) && 6 (author ? book.author === author : true) 7 ); 8 } 9 } 10};
In this resolver:
- The
books
query acceptsgenre
andauthor
as optional arguments. - It filters the
books
array based on these arguments. - If an argument is provided, it filters by that argument; otherwise, it includes all books.
For example, querying for books by a specific author:
graphql1query { 2 books(author: "J.R.R. Tolkien") { 3 title 4 author 5 } 6}
This would return:
JSON1{ 2 "data": { 3 "books": [ 4 { 5 "title": "The Hobbit", 6 "author": "J.R.R. Tolkien" 7 } 8 ] 9 } 10}
Next, we handle mutations to add new entries. Here's how to set up the resolver for adding a book:
TypeScript1const resolvers = { 2 Mutation: { 3 addBook: ( 4 _: unknown, 5 { title, author, publishedDate, genre }: 6 { title: string; author: string; publishedDate: string; genre: string } 7 ) => { 8 const newBook = { id: uuidv4(), title, author, publishedDate, genre }; 9 books.push(newBook); 10 return newBook; 11 } 12 } 13};
This resolver:
- Accepts
title
,author
,publishedDate
, andgenre
as arguments. - Creates a new book object with a unique
id
. - Adds the new book to the
books
array. - Returns the newly added book.
Example mutation request:
graphql1mutation { 2 addBook(title: "1984", author: "George Orwell", publishedDate: "1949", genre: "Dystopian") { 3 id 4 title 5 author 6 } 7}
Response:
JSON1{ 2 "data": { 3 "addBook": { 4 "id": "unique-id", 5 "title": "1984", 6 "author": "George Orwell", 7 "publishedDate": "1949", 8 "genre": "Dystopian" 9 } 10 } 11}
Finally, let's see how to fetch data using the fetch
API in TypeScript. We'll start by querying the list of books and then adding a new book.
TypeScript1import fetch from 'node-fetch'; 2 3const fetchBooks = async () => { 4 const query = ` 5 query { 6 books { 7 title 8 author 9 publishedDate 10 genre 11 } 12 } 13 `; 14 15 const url = 'http://localhost:4000/'; 16 17 try { 18 const response = await fetch(url, { 19 method: 'POST', 20 headers: { 21 'Content-Type': 'application/json', 22 }, 23 body: JSON.stringify({ query }), 24 }); 25 26 const data = await response.json(); 27 console.log('Books:', JSON.stringify(data, null, 2)); 28 } catch (error) { 29 console.error('Error:', error); 30 } 31}; 32 33fetchBooks();
Then, let's continue trying to add a book using the proper mutation:
TypeScript1const addBook = async (title: string, author: string, publishedDate: string, genre: string) => { 2 const mutation = ` 3 mutation { 4 addBook(title: "${title}", author: "${author}", publishedDate: "${publishedDate}", genre: "${genre}") { 5 id 6 title 7 author 8 publishedDate 9 genre 10 } 11 } 12 `; 13 14 const url = 'http://localhost:4000/'; 15 16 try { 17 const response = await fetch(url, { 18 method: 'POST', 19 headers: { 20 'Content-Type': 'application/json', 21 }, 22 body: JSON.stringify({ query: mutation }), 23 }); 24 25 const data = await response.json(); 26 console.log('Added Book:', JSON.stringify(data, null, 2)); 27 } catch (error) { 28 console.error('Error:', error); 29 } 30}; 31 32addBook('1984', 'George Orwell', '1949', 'Dystopian');
Both examples demonstrate how to send queries and mutations to the GraphQL server and handle the responses.
In this lesson, we covered how to enhance your GraphQL API by using advanced arguments in queries and mutations. You learned how to:
- Define a GraphQL schema with advanced arguments.
- Implement resolvers to handle these arguments.
- Perform queries and mutations via the
fetch
API in TypeScript.
This knowledge allows you to create more flexible and powerful GraphQL APIs. Now, it's time for you to practice these concepts with the exercises that follow, which will help you solidify your understanding and build confidence in using advanced GraphQL features.
Good luck, and happy coding!