GraphQL basics. Part 4. Creating an Apollo Server with MongoDB Database

GraphQL basics. Part 4. Creating an Apollo Server with MongoDB Database

Apollo Server is an open source, spec-compliant GraphQL server that is compatible with any GraphQL client. This tutorial assumes you are familiar with the command line, JavaScript, and MongoDB. Also you must have MongoDB database installed. Instructions for installing it are on the official site.

Creating a new project

First, create a new project:

  1. mkdir graphql-server-example
  2. cd graphql-server-example
  3. npm init -y

Install dependencies: npm install apollo-server graphql mongoose

  1. apollo-server - Apollo Server Core Library.
  2. graphql - a library for building a GraphQL schema and making queries to it.
  3. mongoose - object Data Modeling (ODM) library for MongoDB and Node.

Create an empty index.js file in the project root directory: touch index.js. For simplicity, index.js will contain all the application code.

Database connection

Let's create a connection to the test database books in our locally running MongoDB instance.

const mongoose = require("mongoose");

mongoose.connect("mongodb://localhost:27017/books", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: true,
});

const db = mongoose.connection;
db.on("error", () => {
  console.error("database connection error");
});
db.once("open", () => {
  console.log("connected to database");
});

Creating MongoDB Data Schema

The database will store information about the books, namely the title of the book and the name of the author. Let's create a schema and a Book model:

const bookSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
  },
  author: {
    type: String,
    required: true,
  },
});

const Book = mongoose.model("Book", bookSchema);

GraphQL schema

The GraphQL server will support the following operations:

  1. Retrieving information about all books in the database.
  2. Getting information about a specific book by its identifier.
  3. Adding a book to the database.
  4. Deleting a book with a given identifier.

To do this, we describe the GraphQL schema:

const { ApolloServer, gql } = require("apollo-server");

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

  type Query {
    book(id: ID!): Book
    books: [Book!]!
  }

  type Mutation {
    addBook(title: String!, author: String!): Book!
    removeBook(id: ID!): Book
  }
`;

Resolvers definition

Let's define converters for getting all books, book with the specified id, adding and deleting book:

const resolvers = {
  Query: {
    book: async (_, args) => {
      try {
        return await Book.findById(args.id).exec();
      } catch (error) {
        throw error;
      }
    },
    books: async () => {
      try {
        return await Book.find({}).exec();
      } catch (error) {
        throw error;
      }
    },
  },
  Mutation: {
    addBook: async (_, args) => {
      try {
        const { title, author } = args;
        return await Book.create({ title, author });
      } catch (error) {
        throw error;
      }
    },
    removeBook: async (_, args) => {
      try {
        return await Book.findByIdAndRemove(args.id).exec();
      } catch (error) {
        throw error;
      }
    },
  },
};

Running the GraphQL Server

Let's create a GraphQL server instance and run it:

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

Below is the complete content of the index.js file:

const { ApolloServer, gql } = require("apollo-server");
const mongoose = require("mongoose");

mongoose.connect("mongodb://localhost:27017/books", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: true,
});

const db = mongoose.connection;
db.on("error", () => {
  console.error("database connection error");
});
db.once("open", () => {
  console.log("connected to database");
});

const bookSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
  },
  author: {
    type: String,
    required: true,
  },
});

const Book = mongoose.model("Book", bookSchema);

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

  type Query {
    book(id: ID!): Book
    books: [Book!]!
  }

  type Mutation {
    addBook(title: String!, author: String!): Book!
    removeBook(id: ID!): Book
  }
`;

const resolvers = {
  Query: {
    book: async (_, args) => {
      try {
        return await Book.findById(args.id).exec();
      } catch (error) {
        throw error;
      }
    },
    books: async () => {
      try {
        return await Book.find({}).exec();
      } catch (error) {
        throw error;
      }
    },
  },
  Mutation: {
    addBook: async (_, args) => {
      try {
        const { title, author } = args;
        return await Book.create({ title, author });
      } catch (error) {
        throw error;
      }
    },
    removeBook: async (_, args) => {
      try {
        return await Book.findByIdAndRemove(args.id).exec();
      } catch (error) {
        throw error;
      }
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

Run the command node index.js to start the server. Navigate to localhost:4000, you should see the GraphQL Playground.

Creating queries and mutations

Let's add the book to the database. To do this, let's create a mutation:

mutation AddBook {
  addBook(title: "First book", author: "Me") {
    id
    title
    author
  }
}

Result:

{
  "data": {
    "addBook": {
      "id": "608b0dc66fa60fcd732ab173",
      "title": "First book",
      "author": "Me"
    }
  }
}

Let's get a list of all books:

query AllBooks {
  books {
    id
    title
    author
  }
}

Result:

{
  "data": {
    "books": [
      {
        "id": "608b0dc66fa60fcd732ab173",
        "title": "First book",
        "author": "Me"
      }
    ]
  }
}

Let's get a book with a given id:

query BookByID {
  book(id: "608b0dc66fa60fcd732ab173") {
    title
    author
  }
}

Result:

{
  "data": {
    "book": {
      "title": "First book",
      "author": "Me"
    }
  }
}

Let's delete the book with the given id:

mutation RemoveBook {
  removeBook(id: "608b0dc66fa60fcd732ab173") {
    id
    title
    author
  }
}

Result:

{
  "data": {
    "removeBook": {
      "id": "608b0dc66fa60fcd732ab173",
      "title": "First book",
      "author": "Me"
    }
  }
}