9 min read

In this article by Samer Buna author of the book Learning GraphQL and Relay, we’re mostly going to be talking about how an API is nothing without access to a database. Let’s set up a local MongoDB instance, add some data in there, and make sure we can access that data through our GraphQL schema.

(For more resources related to this topic, see here.)

MongoDB can be locally installed on multiple platforms. Check the documentation site for instructions for your platform (https://docs.mongodb.com/manual/installation/).

For Mac, the easiest way is probably Homebrew:

~ $ brew install mongodb

Create a db folder inside a data folder. The default location is /data/db:

~ $ sudo mkdir -p /data/db

Change the owner of the /data folder to be the current logged-in user:

~ $ sudo chown -R $USER /data

Start the MongoDB server:

~ $ mongod

If everything worked correctly, we should be able to open a new terminal and test the mongo CLI:

~/graphql-project $ mongo

MongoDB shell version: 3.2.7
connecting to: test
> db.getName()
test
>

We’re using MongoDB version 3.2.7 here. Make sure that you have this version or newer versions of MongoDB.

Let’s go ahead and create a new collection to hold some test data. Let’s name that collection users:

> db.createCollection("users")"
{ "ok" : 1 }

Now we can use the users collection to add documents that represent users. We can use the MongoDB insertOne() function for that:

> db.users.insertOne({
    firstName: "John","
    lastName: "Doe","
    email: "john@example.com"
  })

We should see an output like:

{
  "acknowledged" : true,
  "insertedId" : ObjectId("56e729d36d87ae04333aa4e1")
}

Let’s go ahead and add another user:

> db.users.insertOne({
    firstName: "Jane","
    lastName: "Doe","
    email: "jane@example.com"
  })

We can now verify that we have two user documents in the users collection using:

> db.users.count()
2

MongoDB has a built-in unique object ID which you can see in the output for insertOne().

Now that we have a running MongoDB, and we have some test data in there, it’s time to see how we can read this data using a GraphQL API.

To communicate with a MongoDB from a Node.js application, we need to install a driver. There are many options that we can choose from, but GraphQL requires a driver that supports promises. We will use the official MongoDB Node.js driver which supports promises. Instructions on how to install and run the driver can be found at: https://docs.mongodb.com/ecosystem/drivers/node-js/.

To install the MongoDB official Node.js driver under our graphql-project app, we do:

~/graphql-project $ npm install --save mongodb
└─┬ mongodb@2.2.4

We can now use this mongodb npm package to connect to our local MongoDB server from within our Node application. In index.js:

const mongodb = require('mongodb');
const assert = require('assert');

const MONGO_URL = 'mongodb'://localhost:27017/test';

mongodb.MongoClient.connect(MONGO_URL, (err, db) => {
  assert.equal(null, err);
  console.log('Connected' to MongoDB server');

  // The readline interface code
});

The MONGO_URL variable value should not be hardcoded in code like this. Instead, we can use a node process environment variable to set it to a certain value before executing the code. On a production machine, we would be able to use the same code and set the process environment variable to a different value.

Use the export command to set the environment variable value:

export MONGO_URL=mongodb://localhost:27017/test

Then in the Node code, we can read the exported value by using:

process.env.MONGO_URL

If we now execute the node index.js command, we should see the Connected to MongoDB server line right before we ask for the Client Request.

At this point, the Node.js process will not exit after our interaction with it. We’ll need to force exit the process with Ctrl + C to restart it.

Let’s start our database API with a simple field that can answer this question: How many total users do we have in the database?

The query could be something like:

{ usersCount }

To be able to use a MongoDB driver call inside our schema main.js file, we need access to the db object that the MongoClient.connect() function exposed for us in its callback. We can use the db object to count the user documents by simply running the promise:

db.collection('users').count()
  .then(usersCount => console.log(usersCount));

Since we only have access to the db object in index.js within the connect() function’s callback, we need to pass a reference to that db object to our graphql() function. We can do that using the fourth argument for the graphql() function, which accepts a contextValue object of globals, and the GraphQL engine will pass this context object to all the resolver functions as their third argument. Modify the graphql function call within the readline interface in index.js to be:

graphql.graphql(mySchema, inputQuery, {}, { db }).then(result => {
  console.log('Server' Answer :', result.data);
  db.close(() => rli.close());
});

The third argument to the graphql() function is called the rootValue, which gets passed as the first argument to the resolver function on the top level type. We are not using that feature here.

We passed the connected database object db as part of the global context object. This will enable us to use db within any resolver function.

Note also how we’re now closing the rli interface within the callback for the operation that closes the db. We should not leave any open db connections behind.

Here’s how we can now use the resolver third argument to resolve our usersCount top-level field with the db count() operation:

fields: {
  // "hello" and "diceRoll"..."
  usersCount: {
    type: GraphQLInt,
    resolve: (_, args, { db }) =>
      db.collection('users').count()
  }
}

A couple of things to notice about this code:

  • We destructured the db object from the third argument for the resolve() function so that we can use it directly (instead of context.db).
  • We returned the promise itself from the resolve() function. The GraphQL executor has native support for promises. Any resolve() function that returns a promise will be handled by the executor itself. The executor will either successfully resolve the promise and then resolve the query field with the promise-resolved value, or it will reject the promise and return an error to the user.

We can test our query now:

~/graphql-project $ node index.js
Connected to MongoDB server
Client Request: { usersCount }
Server Answer : { usersCount: 2 }
*** #GitTag: chapter1-setting-up-mongodb ***

Setting up an HTTP interface

Let’s now see how we can use the graphql() function under another interface, an HTTP one.

We want our users to be able to send us a GraphQL request via HTTP. For example, to ask for the same usersCount field, we want the users to do something like:

/graphql?query={usersCount}

We can use the Express.js node framework to handle and parse HTTP requests, and within an Express.js route, we can use the graphql() function. For example (don’t add these lines yet):

const app = express();

app.use('/graphql', (req, res) => {
  // use graphql.graphql() to respond with JSON objects
});

However, instead of manually handling the req/res objects, there is a GraphQL Express.js middleware that we can use, express-graphql. This middleware wraps the graphql() function and prepares it to be used by Express.js directly. Let’s go ahead and bring in both the Express.js library and this middleware:

~/graphql-project $ npm install --save express express-graphql
├─┬ express@4.14.0
└─┬ express-graphql@0.5.3

In index.js, we can now import both express and the express-graphql middleware:

const graphqlHTTP = require('express-graphql');
const express = require('express');

const app = express();

With these imports, the middleware main function will now be available as graphqlHTTP(). We can now use it in an Express route handler. Inside the MongoClient.connect() callback, we can do:

app.use('/graphql', graphqlHTTP({
    schema: mySchema,
    context: { db }
  }));

  app.listen(3000, () =>
    console.log('Running Express.js on port 3000')
  );

Note that at this point we can remove the readline interface code as we are no longer using it. Our GraphQL interface from now on will be an HTTP endpoint.

The app.use line defines a route at /graphql and delegates the handling of that route to the express-graphql middleware that we imported. We pass two objects to the middleware, the mySchema object, and the context object. We’re not passing any input query here because this code just prepares the HTTP endpoint, and we will be able to read the input query directly from a URL field.

The app.listen() function is the call we need to start our Express.js app. Its first argument is the port to use, and its second argument is a callback we can use after Express.js has started.

We can now test our HTTP-mounted GraphQL executor with:

~/graphql-project $ node index.js

Connected to MongoDB server
Running Express.js on port 3000

In a browser window go to:

http://localhost:3000/graphql?query={usersCount}

*** #GitTag: chapter1-setting-up-an-http-interface ***

The GraphiQL editor

The graphqlHTTP() middleware function accepts another property on its parameter object graphiql, let’s set it to true:

app.use('/graphql', graphqlHTTP({
  schema: mySchema,
  context: { db },
  graphiql: true
}));

When we restart the server now and navigate to http://localhost:3000/graphql, we’ll get an instance of the GraphiQL editor running locally on our GraphQL schema:

GraphiQL is an interactive playground where we can explore our GraphQL queries and mutations before we officially use them. GraphiQL is written in React and GraphQL, and it runs completely within the browser.

GraphiQL has many powerful editor features such as syntax highlighting, code folding, and error highlighting and reporting. Thanks to GraphQL introspective nature, GraphiQL also has intelligent type-ahead of fields, arguments, and types.

Put the cursor in the left editor area, and type a selection set:

{
}

Place the cursor inside that selection set and press Ctrl + space. You should see a list of all fields that our GraphQL schema support, which are the three fields that we have defined so far (hello, diceRoll, and usersCount):

If Ctrl +space does not work, try Cmd + space, Alt + space, or Shift + space.

The __schema and __type fields can be used to introspectively query the GraphQL schema about what fields and types it supports.

When we start typing, this list starts to get filtered accordingly. The list respects the context of the cursor, if we place the cursor inside the arguments of diceRoll(), we’ll get the only argument we defined for diceRoll, the count argument.

Go ahead and read all the root fields that our schema support, and see how the data gets reported on the right side with the formatted JSON object:

*** #GitTag: chapter1-the-graphiql-editor ***

Summary

In this article, we learned how to set up a local MongoDB instance, add some data in there, so that we can access that data through our GraphQL schema.

Resources for Article:


Further resources on this subject:


Packt

Share
Published by
Packt

Recent Posts

Top life hacks for prepping for your IT certification exam

I remember deciding to pursue my first IT certification, the CompTIA A+. I had signed…

3 years ago

Learn Transformers for Natural Language Processing with Denis Rothman

Key takeaways The transformer architecture has proved to be revolutionary in outperforming the classical RNN…

3 years ago

Learning Essential Linux Commands for Navigating the Shell Effectively

Once we learn how to deploy an Ubuntu server, how to manage users, and how…

3 years ago

Clean Coding in Python with Mariano Anaya

Key-takeaways:   Clean code isn’t just a nice thing to have or a luxury in software projects; it's a necessity. If we…

3 years ago

Exploring Forms in Angular – types, benefits and differences   

While developing a web application, or setting dynamic pages and meta tags we need to deal with…

3 years ago

Gain Practical Expertise with the Latest Edition of Software Architecture with C# 9 and .NET 5

Software architecture is one of the most discussed topics in the software industry today, and…

3 years ago