Lesson 1
Adding Mutations for Data Manipulation
Introduction

Welcome to the first lesson of our "GraphQL Mutations and Advanced Apollo Server" course, part of the "Comprehensive Intro to GraphQL in TypeScript" series. In this lesson, you'll learn how to add mutations, which will allow you to modify data on the server.

Revisiting Apollo Server Basics

We'll start with a quick revision of key components without introducing mutations.

  1. Import Modules: Import the necessary modules, including Apollo Server for setting up the server and uuid for generating unique IDs.

    TypeScript
    1import { ApolloServer, gql } from 'apollo-server'; 2import { v4 as uuidv4 } from 'uuid';
  2. Define Schema: Define the GraphQL schema with a Book type and a Query type to fetch books data.

    TypeScript
    1const typeDefs = gql` 2 type Book { 3 id: ID! 4 title: String! 5 author: String! 6 } 7 8 type Query { 9 books: [Book] 10 book(id: ID!): Book 11 } 12`;
  3. Sample Data: Provide some sample book data to be served by our query.

    TypeScript
    1let books = [ 2 { id: '1', title: 'The Hobbit', author: 'J.R.R. Tolkien' }, 3 { id: '2', title: 'Harry Potter', author: 'J.K. Rowling' }, 4];
  4. Define Resolvers: Specify how each field in the schema maps to the data provided.

    TypeScript
    1const resolvers = { 2 Query: { 3 books: () => books, 4 book: (_: any, args: { id: string }) => books.find(book => book.id === args.id), 5 }, 6};
  5. Initialize and Start Server: Create an instance of ApolloServer and start it.

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

Mutations in GraphQL work like creating, updating, or deleting data records. Let's define the mutations in our schema.

  1. AddBook Mutation: Define a mutation to add a new book by specifying a title and author.

    TypeScript
    1type Mutation { 2 addBook(title: String!, author: String!): Book 3}
  2. DeleteBook Mutation: Define a mutation to delete a book by specifying its ID.

    TypeScript
    1type Mutation { 2 deleteBook(id: ID!): Book 3}
Writing Resolvers for Mutations

Resolvers execute the behavior for a given type in the schema.

  1. Adding a Book: Resolver function to take the title and author, create a new book with a unique ID, add it to the list, and return the new book.

    TypeScript
    1addBook: (_: any, { title, author }: { title: string, author: string }) => { 2 const newBook = { id: uuidv4(), title, author }; 3 books.push(newBook); 4 return newBook; 5},
  2. Deleting a Book: Resolver function to take the book ID, find and remove the book from the list, and return the deleted book. The splice method removes the book at bookIndex from the books array and assigns the removed book to the variable deletedBook.

    TypeScript
    1deleteBook: (_: any, { id }: { id: string }) => { 2 const bookIndex = books.findIndex(book => book.id === id); 3 if (bookIndex === -1) return null; 4 const [deletedBook] = books.splice(bookIndex, 1); 5 return deletedBook; 6},
Testing Mutations

To test our mutations, we'll use a Node.js script to make HTTP requests to our GraphQL server.

  1. Import Fetch Module: Import the fetch function from node-fetch to make HTTP requests.

    TypeScript
    1import fetch from 'node-fetch';
  2. Define Queries and Mutations: Define the queries and mutations we want to perform for testing.

    TypeScript
    1const queryBooks = ` 2 query { 3 books { 4 id 5 title 6 author 7 } 8 } 9`; 10 11const addBookMutation = (title: string, author: string) => ` 12 mutation { 13 addBook(title: "${title}", author: "${author}") { 14 id 15 title 16 author 17 } 18 } 19`; 20 21const deleteBookMutation = (id: string) => ` 22 mutation { 23 deleteBook(id: "${id}") { 24 id 25 title 26 author 27 } 28 } 29`;
  3. Function to Execute Requests: Create a function to send HTTP requests to the GraphQL server and log the response.

    TypeScript
    1const url = 'http://localhost:4000/'; 2 3const makeRequest = async (query: string) => { 4 try { 5 const response = await fetch(url, { 6 method: 'POST', 7 headers: { 8 'Content-Type': 'application/json', 9 }, 10 body: JSON.stringify({ query }), 11 }); 12 const json = await response.json(); 13 console.log(JSON.stringify(json, null, 2)); 14 } catch (error) { 15 console.error('Error:', error); 16 } 17};
  4. Execute Sample Requests: Run a sequence of requests to query books, add a new book, and delete a book, then observe the changes.

    TypeScript
    1(async () => { 2 console.log("Query all books:"); 3 await makeRequest(queryBooks); 4 5 console.log("Add a new book:"); 6 await makeRequest(addBookMutation("New Book", "New Author")); 7 8 console.log("Query all books after addition:"); 9 await makeRequest(queryBooks); 10 11 console.log("Delete a book:"); 12 await makeRequest(deleteBookMutation("1")); 13 14 console.log("Query all books after deletion:"); 15 await makeRequest(queryBooks); 16})();
Expected Output

When running the script, you should see logged outputs similar to:

JSON
1{ 2 "data": { 3 "books": [ 4 { "id": "1", "title": "The Hobbit", "author": "J.R.R. Tolkien" }, 5 { "id": "2", "title": "Harry Potter", "author": "J.K. Rowling" } 6 ] 7 } 8}
JSON
1{ 2 "data": { 3 "addBook": { 4 "id": "unique-id", 5 "title": "New Book", 6 "author": "New Author" 7 } 8 } 9}
JSON
1{ 2 "data": { 3 "deleteBook": { 4 "id": "1", 5 "title": "The Hobbit", 6 "author": "J.R.R. Tolkien" 7 } 8 } 9}
Review and Next Steps

In this lesson, you learned how to:

  1. Set up a basic Apollo Server.
  2. Define GraphQL schema with mutations.
  3. Write resolver functions for mutations.
  4. Test your mutations using a Node.js script.

Next, you'll get hands-on practice with these concepts through a series of exercises. In the upcoming lessons, we will delve deeper into advanced features and best practices in GraphQL and Apollo Server.

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