Techscrypt

Build a GraphQL API with NodeJS and MongoDB

nodejsgraphql

Introduction

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.

References

  1. GraphQL
  2. Apollo GraphQL
  3. Apollo Data Source MongoDB
  4. MongooseJS
  5. MongoDB

Subscribe to the Newsletter

We will only use your email address to send out the weekly newsletter with the latest updates from Techscrypt.

* indicates required