Create Your GraphQL API to access your mongoDB Database via Apollo Server

If you follow this tutorial from start to finish you will learn how to organise and create a backend for your own data science project yourself. The GraphQL API will expose your database via an Apollo Server instance that is deployed on Heroku.

Yves Boutellier
Towards Data Science

--

Photo by Douglas Lopes on Unsplash

What you can expect

This tutorial is intended for people that know some data science skills like visualising, analysing data, or even applying machine-learning models but that have not prior experience with the creation of an API which connects to a persistent database. If you wanted to have backend skills that make your data science skills more publicly available via an API then here is a further outlook what we will create in this tutorial.

We focus in this tutorial more on the infrastructure side this will include the following steps. We will create a GraphQL schema, define a query and three different mutations. Additionally, we will write some resolver functions. Moreover, to make data persistent we connect to a mongoDB cluster in the cloud. We will create that cluster together, don’t worry if you haven’t done that before. To make our small tutorial real-world applicable we are going to deploy an apollo-server instance to the cloud with heroku. Also deploying the server instance to the cloud will be covered step by step.

Skills unlocked with this tutorial 🔓

If you follow this tutorial from start to finish you will learn how to organise a backend for your own data science project yourself. For example, you could create a dashboard which displays some data which is served by your graphQL API that is served from an apollo-server (that you’ve created) which accesses a database (that you control) in the background. Apollo Servers with GraphQL are really nice to combine information from various resources. You could maintain a database that gives you some data, apollo let’s you access it as you will see in this tutorial. Additionally, you could access different APIs from your favourite data sources. You dashboard only needs to connect to your apollo-server and gets data from the different APIs as well as your database. You could even have multiple databases. I hope you see how powerful this is. So let’s unlock this beast!

Note we are not going to cover what each of the graphQL syntax means. This tutorial focuses on the overarching principles of creating a backend structure for a data driven application with GraphQL, apollo-server, nodejs, heroku and mongoDB.

Basic Setup (Folders and package.json)

First off, create some folders. Go inside your folder, where you have your programming projects and tutorials and create a folder called graphqlAPI. This is the folder where all the code from the tutorial comes inside. Inside of this folder called graphqlAPI create a folder called server. Go inside of this folder and initialise a npm package via npm init-y.

summarised:

  1. mkdir graphqlAPI
  2. cd graphqlAPI
  3. mkdir server
  4. cd server
  5. npm init -y

Open your IDE and on the level of the server folder. Open the package.json file which was created by npm init. We are going to make some changes to this file.Change the file as the following:

  • the line "test": "echo \\"Error: no test specified\\" && exit 1” will be replaced by "start": "nodemon src/index.js"

nodemon will be a package that facilitates development since our changes will be directly be reflected by our server without full restart. But to make use of nodemon we need to install it. Open your terminal inside your IDE and make sure to be in the server folder. Write and enter simply npm i nodemon

In case you want to compare your package.json with mine:

Create folder src

We then create a file called index.js inside a new folder called src .

Additionally we create a folder called types which is located inside the src folder. This is where we are going to store our graphQL schema.

Our folder structure should look like this.

Since we access our graphQL API via an apollo-server we are going to need the apollo-server connection in later steps, thus we require another package. Add this second dependency, write and enter the following in your terminal.

npm i apollo-server

Create our graphQL schema

We write our schema in a file called coin.js inside the types folder and use the following lines for our schema such that we can create, read, update and delete (full CRUD application) our data.

Additionally we create a index.js file inside the types folder to facilitate exporting of our schema. It’s later easier to add and delete additional types if you want to.

Store Data

Right now, we only have a schema but we are missing the database, the resolver functions and the server. Let’s continue with the database. In order to store our data we include an additional dependency for our project called mongoose. It’s a framework to interact with mongoDB.

Write npm i mongoose in order to add it to our dependency.

We create a folder called models and inside we create a file called coin.js

The content of the coin.js looks like this:

Next we add a file called index.js to our models folder

We need resolvers

Resolver functions are a collection of functions that generate the response for a GraphQL query. Since we serve our data from our mongoDB database, we are going to use the models object we created before in the models folder which gives us connection to our database. But we don’t have to include it in this file. Later when we create our apollo server, the model, the schema and the resolver functions are part of the same context. The resolver functions then can access the models. So our task is it to write resolver functions for now.

Let’s create three folders. First create a folder called resolvers inside src . Inside of the resolvers folder create two folders, one is called mutations the other is called queries . If you completed this step, you can create a first file for the queries folder called coins.js

Okay and what are we going to add to our file? This:

Let’s go over it quickly.

It’s an async function that retrieves all ERC20Coins we stored in our database. Calling a resolver function includes three to four arguments.

  1. the first one is parent resolver function if there is one, but we have none, thus we use _ to indicate that
  2. the second parameter are the arguments for the graphQL query. But remember we defined only one query in our schema (see src/types/coin.js) that looks like this: type Query{ coins: [ERC20Coin]} We see that it doesn’t have any arguments, therefore we use an empty object as the second argument
  3. the third arguments is the models object that gives us connection to the mongoDb database I mentioned before.
  4. the fourth argument is called info but you shouldn’t be bothered about this one, because it is only required for advanced use-cases.

Since we have the resolver function for our single query completed, we can proceed with the resolver functions for the mutations.

Mutations

In our schema we defined three different but yet simple mutations: create, update and delete.

We start with the easiest one (although all are easy).

Create

The general structure is the same as for the non-mutation query. But this time the second argument is not empty but the input object we defined in our schema. (If you want you can look it up again src/types/coin.js)

Update

The arguments are slightly different than before, as we defined in our schema that in order to update an ERC20Coin we need an id as well as an input (e.g name). By using the mongoose framework we can access the ERC20Coin in our database that has the id that we are providing in the function called findOne

The next step is about looping through the input object (a plain javascript object) which might contain new updated fields for our database entry, in our case this can potentially be a different name. If you want you can update the schema with more fields. The last steps includes saving the updated model to our database again and we return the new updated model with this function.

Delete

If you check our schema you will know that we only use id and no input object. Like before we use our mongoose framework. This is straight forward, after we’ve done three resolver functions.

Create index.js file for our resolver folders

The three folders resolvers, mutations and queries are missing each an index.js file. Let’s improve that.

Before we create some index.js files I want to point out that the names we are using here to export must match the names we gave the different mutations and the query. E.g the query and mutations in our schema src/types/coin.js look like this.

The names we need to choose for our exports are coins , createCoin , updateCoin , deleteCoin .

Create an index.js file inside src/resolvers/queries with the following content:

Create an index.js file inside src/resolvers/mutations with the following content, again the names createCoin, updateCoin, deleteCoin match the names from the schema.

Additionally we create an index.js file inside src/resolvers

We can use the spread operator ... so that we don’t have to change this file once we add new mutations or queries.

Now that we have our schema as well as functioning code that allows us to retrieve data from a mongoDB database. We are going to proceed with creating a database inside the cloud.

Create a database on cloud.mongodb

We will use for the demonstration and tutorial purposes a database in a free cloud server. Go to https://www.mongodb.com/atlas/database and try it for free. After you signed in:

  • Look for a green button that says create
  • Choose shared cloud
  • Choose a free tier location which is closest to your physical location
  • skip the additional settings until you arrive at the Security Quickstart

There you should choose Username and Password to authenticate your connection. Pick a username and password you can remember and write it down.

screenshot by author

Then you are asked from where you want to connect from.

screenshot by author

Choose connection from local environment and add IP address 0.0.0.0/0 which will allow all IP addresses connect to your database (this is a security issue and not recommended for production, but since this is a tutorial we stick with that because it makes things easier once we use heroku. But remember if you want to create a real world project, take this here seriously and invest some time in your own research on this topic).

When you continue you should see something like this. Click the button that says Connect next to where it says Cluster0

screenshot by author

Take the option that says “connect your application” in this image right here it’s the option in the middle. After you clicked “connect your application” the next page will let you select a driver. Go for Node.js with the latest version.

IMPORTANT copy the connection String! That appears right below.

screenshot by author

Setup to connect to your database via node.js

After you have copied the connection string. Go back to your editor and create a .env file in the server directory. I list here how the folder structure should look like.

inside of this file assign this string you copied from the cloud.mongodb dashboard to a variable called DATABASE_URL

DATABASE_URL = mongodb+srv://...

To make use of this .env file and its variable you need to install a package called dotenv

npm i dotenv

Additionally we should create a new folder inside src called config which contains a file called db.js

Inside of this file db.js we are going to write some basic code to connect to our database. Once that is done, we can finally create our server.

First we require the package to read our .env file we created before.

  • require('dotenv').config();

and this is how we access our variable:

  • const DATABASE_URL = process.env.DATABASE_URL;

We require a package that let’s us connect with the mongoDB database and that wraps its API

  • const mongoose = require('mongoose');

The next step consists of writing a function that let’s us connect to the database which we call connectDB. We export this function as we need it later in combination with our apollo server.

Furthermore, we create an object that helps us logging connection failures.

What we covered so far: We wrote a graphQL schema, wrote resolver functions that fill in queries that are made to our API plus we setup everything to connect to our database. Thus let’s create our apollo server that exposes the API. After that we will deploy this apollo server to heroku.

Create an Apollo Server

Go to your index.js inside the src directory which we created much earlier but is still empty.

We start off by listing all requirements from the code we have written earlier plus the apollo-server package.

Before we connect to our apollo server, we make sure that we are connected to our database:

add connectDB(); to a following line in this file

then we create a server object and start running it

Let’s test it! Go to your terminal inside of your server folder and run npm run start

I included a screenshot of my terminal response and of my browser for you as a reference.

My terminal (first) and my browser at localhost (second)

screenshot by author
screenshot by author

Inside of your Browser press the button that says Query your server

Test your API

We now will explore the functionality of our GraphQL API. We will use all of the mutations and our single query we defined in our schema and write objects into our database from mongoDB. If everything works, we can even deploy our apollo server instance to the cloud with Heroku to make it publicly available.

Write our first query on our server

We defined one query that was called coins, that returns an array with objects of type ERC20Coin.

The result looks like this

We got an empty array. That is not suprising. Since it returns data from our mongoDB database, but we did not write anything to this database yet. So let’s change that.

Write mutations

So let’s add some coins to the database. I chose Ethereum, Solana, Flux and Helium.

Let’s query again the coins from the database.

Write some more mutations

We only explored one mutation, namely creating entries in our database. Let us try out the deletion of coins and after that updating data of the coins.

Always remember when we wrote our schema, we need to adhere to that. The mutation requires us to provide the id of the coin. The id is from the mongoDB database and references a entity in the database. That way our resolver is able to send the correct instructions to our database.

I decided to delete Ethereum from the database. I know from the query before what the id of the Ethereum database entry is.

Let’s query the entries from our database and see if our changes are reflected by the database.

As we can see the changes are successfully written to the database.

Update

The last type of graphQL API query we did not use so far is the updateCoin mutation. Our schema tells us that we need to provide the id of the document in the database and additionally, we need the input. The input is the data that gets used to update the database entry. I decided to update the name of our Helium entry and extended the String with the symbol (HNT). The mutation returns in case of success the id and the name of the database entry. I included this code snippet here as well

The mutation returns data as mentioned before. And it looks promising.

If we repeat the code from the beginning we can see that we have three entries and our deletion from the Ethereum copy as well as Solana are reflected by our database. Additionally, our update on Helium took place as we can see from the response

Everything behaved as we expected. Our graphQL API is accessible to us and has no bugs. If we want to share our data with others we need a server that other people can access as well. I would suggest that you do this part as well, to get the big picture of creating a backend for a data science project.

Deploy Server on Heroku

Heroku is a cloud platform as a service. We can deploy an app to heroku and then our app is available through the cloud. To upload our apollo server we need an account. Please sign up or login with heroku. Then choose to create a new app.

screenshot by author

Then choose a name (must be unique to everybody) and a server location. By the time of this writing there are for free tier only two locations available Europe and the United States.

Since we are deploying our app via the command line interface (CLI) you need to install it yourself, if you haven’t used it before. But it’s pretty simple see this following link https://devcenter.heroku.com/articles/heroku-cli or for macOS you can install the CLI with minimal effort, under the assumption that you have brew installed. Write and execute inside your terminal:

brew tap heroku/brew && brew install heroku

After you have done that. We proceed with initialising and deploying our apollo server with heroku.

Next use heroku login (it uses the CLI to login to heroku)

then make sure the current folder in your terminal is server from our project. I list the following steps you need to undertake in correct order.

  1. git init
  2. heroku git:remote -a <yourprojectName>
  3. git add -A (stage your files)
  4. git commit -m "first commit"

But do not push to heroku yet, because we need to store the DATABASE_URL from .env in our heroku app dashboard under Config Vars

On the left side you add DATABASE_URL and for the value take your connection string for your mongoDB database.

screenshot by author

After you have added this pair you can push your project to heroku.

git push heroku master

After you successfully pushed it to the remote repository of heroku you can open your app on the top right corner.

screenshot by author

It will be some generic Apollo Page that says you can now make some post requests. Wow you finished.

Closing words

If I were you, I’d be proud on what I did. Now you have a bigger picture of which skills you need to make your data driven application accessible to everybody. Which dashboard you had created do you wanted to share with others? Allowing others on the internet to watch your dashboard is a challenging further exercise but within reach for you after completed everything here.

A simpler exercise:

You can try to access your API via postman. The requests content is the same as before in the playground, with the only difference that you now send the request through the world wide web.

--

--