Building GraphQL APIs with Apollo Server and Express.js

GraphQL is a powerful query language for APIs and a runtime for executing those queries with your existing data. It allows clients to request exactly the data they need, making APIs more flexible and efficient. Apollo Server is a popular, open-source implementation of GraphQL that integrates seamlessly with Express.js.In this chapter, we will explore how to build GraphQL APIs using Apollo Server and Express.js. Starting from the basics of GraphQL and Apollo, we will dive deep into advanced features such as query handling, mutations, subscriptions, and error handling. Every section will include easy-to-understand explanations and detailed examples to ensure complete comprehension.

Introduction to GraphQL

What is GraphQL?

GraphQL is a query language developed by Facebook that offers the following benefits:

  1. Flexible Queries: Clients can request only the data they need.
  2. Single Endpoint: All operations (queries, mutations, subscriptions) use a single /graphql endpoint.
  3. Strongly Typed Schema: Provides a structured schema that defines data and operations.

GraphQL vs REST

FeatureGraphQLREST
Data FetchingFlexible queriesFixed endpoints
OverfetchingAvoided, as clients specify dataMay fetch unnecessary data
SchemaStrongly typedNo built-in schema

Setting Up an Express.js Application

Install Dependencies

				
					npm init -y
npm install express graphql apollo-server-express

				
			

Basic Express.js Application

				
					const express = require('express');
const { ApolloServer } = require('apollo-server-express');

const app = express();

// Placeholder middleware
app.get('/', (req, res) => {
  res.send('Welcome to Express.js with Apollo Server!');
});

app.listen(4000, () => {
  console.log('Server running on http://localhost:4000');
});

				
			

Setting Up Apollo Server

Create a Simple GraphQL Schema

				
					const { gql } = require('apollo-server-express');

// Define the schema
const typeDefs = gql`
  type Query {
    hello: String
  }
`;

// Define the resolver
const resolvers = {
  Query: {
    hello: () => 'Hello, GraphQL!',
  },
};

// Set up Apollo Server
const server = new ApolloServer({ typeDefs, resolvers });

server.start().then(() => {
  server.applyMiddleware({ app });
  app.listen(4000, () => {
    console.log(`Server running at http://localhost:4000${server.graphqlPath}`);
  });
});

				
			

Explanation

  1. Schema Definition: typeDefs defines the structure of the data.
  2. Resolvers: Handle requests and return data.
  3. Apollo Middleware: Integrates Apollo Server with Express.js.

Handling Queries

Example: Fetching Data

Modify the schema to fetch a list of books:

				
					const typeDefs = gql`
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;

const resolvers = {
  Query: {
    books: () => [
      { title: 'The Alchemist', author: 'Paulo Coelho' },
      { title: '1984', author: 'George Orwell' },
    ],
  },
};

				
			

Query Example

Send a query:

				
					query {
  books {
    title
    author
  }
}

				
			

Expected result:

				
					{
  "data": {
    "books": [
      { "title": "The Alchemist", "author": "Paulo Coelho" },
      { "title": "1984", "author": "George Orwell" }
    ]
  }
}

				
			

Handling Mutations

Example: Adding a New Book

Update schema with a mutation:

				
					const books = [];

const typeDefs = gql`
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }

  type Mutation {
    addBook(title: String, author: String): Book
  }
`;

const resolvers = {
  Query: {
    books: () => books,
  },
  Mutation: {
    addBook: (_, { title, author }) => {
      const newBook = { title, author };
      books.push(newBook);
      return newBook;
    },
  },
};

				
			

Mutation Example

Send a mutation:

				
					mutation {
  addBook(title: "Sapiens", author: "Yuval Noah Harari") {
    title
    author
  }
}

				
			

Expected result:

				
					{
  "data": {
    "addBook": {
      "title": "Sapiens",
      "author": "Yuval Noah Harari"
    }
  }
}

				
			

Error Handling in Apollo Server

Custom Error Handling

				
					const resolvers = {
  Query: {
    hello: () => {
      throw new Error('Custom error message');
    },
  },
};

				
			

Result:

				
					{
  "errors": [
    {
      "message": "Custom error message"
    }
  ]
}

				
			

Use Apollo’s formatError to customize error responses:

				
					const server = new ApolloServer({
  typeDefs,
  resolvers,
  formatError: (err) => ({
    message: err.message,
    status: err.extensions.code || 500,
  }),
});

				
			

Integrating with a Database

Using MongoDB

				
					npm install mongoose

				
			

Update resolvers to fetch data from MongoDB:

				
					const mongoose = require('mongoose');
const Book = mongoose.model('Book', { title: String, author: String });

mongoose.connect('mongodb://localhost:27017/graphql_books', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

const resolvers = {
  Query: {
    books: async () => await Book.find(),
  },
  Mutation: {
    addBook: async (_, { title, author }) => {
      const newBook = new Book({ title, author });
      await newBook.save();
      return newBook;
    },
  },
};

				
			

Testing and Debugging

Using GraphQL Playground

Apollo Server includes a GraphQL Playground at /graphql for testing queries and mutations.

Writing Unit Tests

Use jest or mocha to write tests for your GraphQL resolvers:

				
					test('fetches books', async () => {
  const response = await query({ query: gql`{ books { title author } }` });
  expect(response.data.books).toEqual(expect.any(Array));
});

				
			

Building GraphQL APIs with Apollo Server and Express.js simplifies the process of creating efficient, scalable, and flexible APIs. Setting up Apollo Server with Express.js. Handling queries, mutations, and subscriptions. Integrating a database and managing errors. By following these concepts, you can build modern APIs that meet the demands of complex applications while ensuring an exceptional developer and user experience. Happy coding !❤️

Table of Contents