Build a GraphQL API with NodeJS and MongoDB
nodejsgraphqlIntroduction #
GraphQL is a query language for APIs. It allows the client to ask for which data is required and produces a predictable result. Learn more at https://graphql.org
The main advantage with GraphQL when compared to REST APIs is that it allows us to get all the relevant data with a single request, rather than multiple requests. For example with a REST API, there might be one request to fetch information about an entity, and additional requests to fetch information about dependent entities. GraphQL also provides a reliable type system that enables clients to expect predictable responses from the service.
This article describes one way to setup a NodeJS API with GraphQL and connect it to a MongoDB database.
Project setup #
In this example we will setup a service that allows us to manage employees. One of the easiest ways to setup a GraphQL API with NodeJS is to use Apollo.
The initial project folder can be setup using
mkdir node-graphql-project
cd node-graphql-project
npm init
Then install the Apollo GraphQL dependencies using
npm install apollo-server graphql
GraphQL Server #
Create a file called index.js
at the root of the project folder, and define the GraphQL Schema. This schema defines the data structure for Employee
and the query to return multiple employees.
gql`
type Employee {
firstName: String
lastName: String
}
type Query {
employees: [Employee]
}
`
For now, the employee database can be a simple json object. This will be replaced by a MongoDB database in a few more steps later. The resolver function defines the data that should be associated with each type. In this case, we will map the employees query to the employees object.
Here is what the index.js
file looks like at this point.
const { ApolloServer, gql } = require("apollo-server");
const { ApolloServerPluginLandingPageLocalDefault } = require("apollo-server-core");
const typeDefs =
const employees = [
{
firstName: "Homer",
lastName: "Simpson",
},
{
firstName: "Philip",
lastName: "Fry",
},
];
const resolvers = {
Query: {
employees: () => employees,
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
csrfPrevention: true,
cache: "bounded",
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
});
// The `listen` method launches a web server.
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Now we can run the server by running the command
node index.js
There are multiple ways to take this code for a test drive.
Access the UI at http://localhost:4000/ and execute the query to fetch employees.
query ExampleQuery {
employees {
firstName
lastName
}
}
Or run a curl command
curl --request POST --header 'content-type: application/json' --url http://localhost:4000/ --data '{"query":"query ExampleQuery {\n employees {\n firstName\n lastName \n}\n}"}'
MongoDB Data Source #
We can connect the service to a MongoDB database by adding the Apollo data source for MongoDB, and mongoose for object modelling.
npm install mongoose apollo-datasource-mongodb
We can connect to MongoDB using the connection string. (Install MongoDB form here)
await mongoose.connect('mongodb://localhost:27017/test');
Now we need to define the data source for Employees by defining the class
class Employees extends MongoDataSource {
async getEmployees() {
return await this.model.find();
}
}
And the employee model schema definition for mongoose
const EmployeeModel = mongoose.model("Employee", {
firstName: String,
lastName: String,
});
These are tied together in the data sources definition and can be passed to the Apollo server using the dataSources
configuration parameter.
const dataSources = () => ({
employees: new Employees(EmployeeModel),
});
This is what the index.js
file looks like at this point.
const { ApolloServer, gql } = require("apollo-server");
const {
ApolloServerPluginLandingPageLocalDefault,
} = require("apollo-server-core");
const { MongoDataSource } = require("apollo-datasource-mongodb");
const mongoose = require("mongoose");
const typeDefs = gql`
type Employee {
firstName: String
lastName: String
}
type Query {
employees: [Employee]
}
`;
const EmployeeModel = mongoose.model("Employee", {
firstName: String,
lastName: String,
});
const resolvers = {
Query: {
employees: async (_, _args, { dataSources: { employees } }) => {
return employees.getEmployees();
},
},
};
const dataSources = () => ({
employees: new Employees(EmployeeModel),
});
class Employees extends MongoDataSource {
async getEmployees() {
return await this.model.find();
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources,
csrfPrevention: true,
cache: "bounded",
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
});
MONGODB_URL = "mongodb://localhost:27017/test";
async function connectMongodb() {
await mongoose.connect(MONGODB_URL);
console.log("Connected to database successfully 🎉");
}
(async () => {
// Connect to DB
try {
await connectMongodb();
} catch (e) {
console.error(e);
throw new Error(`Unable to connect to database`);
}
// Start Graphql Server if connection to DB is successful
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
})();
Note the use of anonymous IIFE async
function. This allows us to await
the database connection before starting the server.
We can again run the server with the command node index.js
and should get back an empty response.
The next step is creating a mutation that will allow us to add data to the database.
GraphQL Mutation #
We can define the GQL Mutation schema as
type Mutation {
CreateEmployee(firstName: String!, lastName: String!): Employee
}
Add the data source method
async createEmployee({ firstName, lastName }) {
return await this.model.create({ firstName, lastName });
}
And the corresponding resolver as
Mutation: {
CreateEmployee: (
_root,
{ firstName, lastName },
{ dataSources: { employees } }
) => {
return employees.createEmployee({ firstName, lastName });
},
}
More info about resolver arguments can be found here
The final index.js
looks like this
const { ApolloServer, gql } = require("apollo-server");
const {
ApolloServerPluginLandingPageLocalDefault,
} = require("apollo-server-core");
const { MongoDataSource } = require("apollo-datasource-mongodb");
const mongoose = require("mongoose");
const typeDefs = gql`
type Employee {
firstName: String
lastName: String
}
type Query {
employees: [Employee]
}
type Mutation {
CreateEmployee(firstName: String!, lastName: String!): Employee
}
`;
const EmployeeModel = mongoose.model("Employee", {
firstName: String,
lastName: String,
});
const resolvers = {
Query: {
employees: async (_, _args, { dataSources: { employees } }) => {
return employees.getEmployees();
},
},
Mutation: {
CreateEmployee: (
_root,
{ firstName, lastName },
{ dataSources: { employees } }
) => {
return employees.createEmployee({ firstName, lastName });
},
},
};
const dataSources = () => ({
employees: new Employees(EmployeeModel),
});
class Employees extends MongoDataSource {
async getEmployees() {
return await this.model.find();
}
async createEmployee({ firstName, lastName }) {
return await this.model.create({ firstName, lastName });
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources,
csrfPrevention: true,
cache: "bounded",
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
});
MONGODB_URL = "mongodb://localhost:27017/test";
async function connectMongodb() {
await mongoose.connect(MONGODB_URL);
console.log("Connected to database successfully 🎉");
}
(async () => {
// Connect to DB
try {
await connectMongodb();
} catch (e) {
console.error(e);
throw new Error(`Unable to connect to database`);
}
// Start Graphql Server if connection to DB is successful
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
})();
Now we can create a new Employee using this Mutation
mutation Mutation($firstName: String!, $lastName: String!) {
CreateEmployee(firstName: $firstName, lastName: $lastName) {
firstName
lastName
}
}
Similarly, mutations to update or delete records can be created as required.
Summary #
In this article, we took a look at an example of how to setup a GraphQL server using NodeJS and connect it to a MongoDB Database using Apollo. Note that all the code in this example is written in a single index.js
file for keeping the demo simple. Ideally in a real world scenario, appropriate folder structure would be setup with separate files for things like the GraphQL Schema, the Mongoose models, Resolvers, Data sources, etc.