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:
mkdir graphql-server-example
cd graphql-server-example
npm init -y
Install dependencies: npm install apollo-server graphql mongoose
apollo-server
- Apollo Server Core Library.graphql
- a library for building a GraphQL schema and making queries to it.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:
- Retrieving information about all books in the database.
- Getting information about a specific book by its identifier.
- Adding a book to the database.
- 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"
}
}
}