How to build a GraphQl API from scratch with Node JS, Express, and MongoDB
Tags: node, graphql, mongodb, beginners
Published: 2020-04-03 18:33:07

In this guide, we will build from scratch an API with GraphQL, Node JS, Express, and MongoDB. So, let's start by answering an important question: what is GraphQL?

- [What is GraphQL?](#what-is-graphql)
- [Setting up the GraphQL API](#setting-up-the-graphql-api)
- [GraphQL Schema](#graphql-schema)
- [Creating Mongoose Models](#creating-mongoose-models)
- [GraphQl resolver](#graphql-resolver)
- [Create the server and an endpoint](#create-the-server-and-an-endpoint)
- [Connect the API to MongoDB](#connect-the-api-to-mongodb)
- [Test the API with GraphiQL](#test-the-api-with-graphiql)

_Originally posted on [my blog](https://www.ibrahima-ndaw.com/blog/graphql-api-express-mongodb/)_

## What is GraphQL?

GraphQL is a query language created by Facebook. It's an alternative to the REST approach.
So, if you come from the REST world, just keep in mind that GraphQL works differently. It has one single endpoint for all kinds of requests, and the method has to be a post request. GraphQL works with types and fields, and it's really powerful since it provides all or just the needed data.

>Send a GraphQL query to your API and get exactly what you need, nothing more and nothing less.

We will see it in action later, but for now, let's plan our API.

## Setting up the GraphQL API

For the API, we will have the possibility to create articles and store them in MongoDB. And also be able to fetch them back.

To do so, we have to create a new project by running the following command in the terminal.

```shell
    yarn init
```

In this tutorial, I will use `yarn`, you can use `npm` if you want too. It's really up to you

Next, structure your project as follow:

```
├── node_modules
├── graphql
|  ├── resolvers
|  |  └── index.js
|  └── schema
|     └── index.js
├── models
|  └── article.js
├── app.js
├── nodemon.json
├── package.json
└── yarn.lock
```

As you can see, we have a `graphql` folder that keeps the schema and the resolvers of the API.

Next, we have a `models` folder which holds what an article should looks like and last but not the least, a `nodemon.json` file to hold our environment variables and the entry point of the server `app.js`.

We have a couple of libraries to install, so I will keep things simple and install the needed one as we progress.

Now, let's run the following commands on the terminal to install `express` and `nodemon`.

```shell
    yarn add express 
```

Next, add `nodemon` as a development dependency. 

```shell
    yarn add nodemon -D
```

With that, we can now add a start script on the `package.json` file to be able as you might guess start the server. 

- `package.json`

```js
{
  "name": "graphql-api",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "nodemon app.js"
  },
  "dependencies": {
    "express": "^4.17.1",
  },
  "devDependencies": {
    "nodemon": "^2.0.2"
  }
}
```

As you can see, here we use `nodemon` to start the server and when a file is added or updated, `nodemon` will react to the update automatically.

We have now the command to start the server, but still, no server to start. We will create the server later, but for now, let's define the schema of the API.

## GraphQL Schema

A schema describes the shape of your data graph. It defines a set of types with fields that are populated from your back-end data stores.

And to create a schema, we need to install the `graphql` package by running on the terminal:

```shell
    yarn add graphql
```

Next, we have to add the following code block to create a GraphQL schema.

- `graphql/schema/index.js`

```js
const { buildSchema } = require('graphql')

module.exports = buildSchema(`

  type Article {
    _id: ID!
    title: String!
    body: String!
    createdAt: String!
  }


  input ArticleInput {
    title: String!
    body: String!
  }

  type Query {
    articles:[Article!]
  }

  type Mutation {
    createArticle(article:ArticleInput): Article
  }

  schema {
    query: Query
    mutation: Mutation
  }
`)
```

To create a schema, we first have to import `buildSchema` from `graphql`, and next create our types.
GraphQL works with types, it supports several [scalar types](https://graphql.org/learn/schema/). 

Here, we have the type `Article` which must have an _id (you have to name it like this due to MongoDB) of type `ID`, a title, a body and a createdAt field of type `String`. 

The exclamation mark `!` just means that the type defined is required, it has to match the type.    

Next, we have an `input` type which defines what the expected input should look like. It's the data entered by the user and will be used to create a new article.

A GraphQL query as the name suggests is used to define a query type. And here, we have an `articles` query to fetch the data. It should return an array, and each article has to match the type `Article`.

Now, to be able to fetch articles, we have to create them first. And to do so, we use a GraphQl `mutation`. It's a query that creates, updates or deletes data in the data store and returns a value. 

And here to create a new article we use the `createArticle` mutation. It receives an object of type `ArticleInput` and returns the created article.

Now we have everything we need to create a schema, the final thing to do is to pass the `Query` and the `Mutation` to the schema. 

And Voilà, we have now a schema.

However, a GraphQl schema is not enough though, we have to create another schema, a model to be precise to ensure that the data send to MongoDB matches the schema defined with GraphQL.

## Creating Mongoose Models

As I mentioned earlier, MongoDB will be used as a database, and to make things easier, we will use `mongoose` to interact with it.

And to install it, we need to run the following command in the terminal.

```shell
  yarn add mongoose
```

Next, we can now create a model for an article.

- `models/article.js`

```js
const mongoose = require('mongoose')

const Schema = mongoose.Schema

const artcleSchema = new Schema({

    title: {
        type: String,
        required: true
    },

    body: {
        type: String,
        required: true
    }

}, { timestamps: true })

module.exports = mongoose.model('Article', artcleSchema)
```

To create a model of data, we first have to import `mongoose` and access to the `Schema` method.
With that, we can now create a schema for a given article. And if you remember, in our GraphQL schema we have some required fields (`!`), hence, here we use the `required` property to follow the schema defined with GraphQL. 

And for the _id, we don't need to add it as a field in the schema since it will be created automatically.
It's the same thing for createdAt, the second argument `timestamps: true` tells to `mongoose` to add a createdAt and updatedAt fields to the schema.  

Now, to create the model, we have to use `mongoose` again and pass as arguments the name of the model and the schema to the `model()` method.

Now, it seems like we have everything we need to create GraphQL resolvers for the API. So, let's do that in the next section. 

![exciting-stuff](https://media.giphy.com/media/fxsW4aPMXNcBsxv1l8/giphy.gif)

## GraphQl resolver

A resolver is a collection of functions that helps generating a response from a GraphQL query. It handles the request and returns a response. And every query or mutation name has to match exactly the name of the resolver function. That means, if we have a query named `articles`, we should have an `articles()` resolver function. 

Now to create resolvers, we have to add this code block below in the `graphql/resolvers/index.js` file.

- `graphql/resolvers/index.js`

```js
const Article = require('../../models/article')

module.exports = {

  articles: async () => {
    try {
       const articlesFetched = await Article.find()
        return articlesFetched.map(article => {
            return {
                ...article._doc,
                _id: article.id,
                createdAt: new Date(article._doc.createdAt).toISOString() }
        })
    }
    catch (error) {
        throw error
    }
 },

  createArticle: async args => {
  try {
    const { title, body } = args.article
    const article = new Article({
        title,
        body
    })
    const newArticle = await article.save()
    return { ...newArticle._doc, _id: newArticle.id }
  }
  catch (error) {
      throw error
  }

 }
}
```

In the GraphQL schema, we have a query named `articles` which return an array of articles. Therefore, we should have here a resolver with the same name.

The `articles` function uses the model created with `mongoose` to send the request to MongoDB. That said, we can now access `.find()` to well as the name suggest fetch all articles from the database.

The returned value is an array, therefore, we have to loop through it and for each object return the document (it's an object which holds the data), override the _id with `mongoose` and convert the createdAt field to a more user-friendly date.

And, as you already know, this operation can take time to complete, that the reason why we use async/await to handle the request. 

For the second resolver function `createArticle`, it handles the mutation defined previously in the GraphQL schema. It receives as an argument the article object, and with that, it creates a new article based on the `Article` model.

And to store it to MongoDB, we just have to use another helper provided by `mongoose`, the `save()` method and return as expected in the GraphQL schema the newly created article.

By the way, the response sent by MongoDB has some metadata that's why for both functions, I return the `_doc` property directly.

With that change, we have now a schema and resolvers for our GraphQL API, it's pretty much what we need to move to the next section and create a server and an endpoint.

## Create the server and an endpoint

In the `package.json` file, we have a script to start the server. And it starts with the `app.js` file, thus we need to update that file a bit to have a real server.

Before writing the logic of creating a server, we need to install `express-graphql` which is the glue between `graphql` and `express`.

And to install it, we have to run the following command in the terminal.

```shell
  yarn add express-graphql
```

Next, add this code block to `app.js`

- `app.js`

```js
const express = require('express')
const graphqlHttp = require('express-graphql')
const graphqlSchema = require('./graphql/schema')
const graphqlResolvers = require('./graphql/resolvers')

const app = express()

app.use('/graphql', graphqlHttp({
    schema:graphqlSchema,
    rootValue:graphqlResolvers,
    graphiql: true
}))

app.listen(3000, () => console.log('Server is running on localhost:3000'))
```

As you can see, here we import the schema and resolvers created earlier. And to use them, we need `graphqlHttp` (you can name it whatever you want). It's a method provided by `express-graphql` that expects some options. Here, it receives the schema and the resolver, I also enabled `graphiql` which is a cool tool for testing queries. 

The endpoint for all requests will be `/graphql`, and to be able to reach that endpoint, we need to start the server and listen to the port 3000.

Great! we've now a working API, but thus far there is still something missing: the API is not connected yet to MongoDB. So, let's fix that in the next section.  

![nice](https://media.giphy.com/media/IUZtGhVO8hZ6w/source.gif)

## Connect the API to MongoDB

If you remember, in the structure folder, we had a `nodemon.json` file, that file will be used now to store our environment variables.

But first, you will need to create a new cluster on [MongoDB Atlas](https://www.mongodb.com/cloud/atlas) and get the username and password of the database.

Next, update the credentials with yours.

- `nodemon.json`.

```js
{
    "env": {
        "MONGO_USER": "your_username",
        "MONGO_PASSWORD": "your_password",
        "MONGO_DB": "your_database"
    }
}
```

Now we have the needed credentials, it's time to connect the API to MongoDB. And to do so, we have to tweak a bit `app.js`.

```js
const express = require('express')
const graphqlHttp = require('express-graphql')
const mongoose = require('mongoose')
const graphqlSchema = require('./graphql/schema')
const graphqlResolvers = require('./graphql/resolvers')

const app = express()

app.use('/graphql', graphqlHttp({
    schema:graphqlSchema,
    rootValue:graphqlResolvers,
    graphiql: true
}))

const uri = `mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}@cluster0-uox7n.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`

const options = {useNewUrlParser: true, useUnifiedTopology: true}

mongoose.connect(uri, options)
        .then(() => app.listen(3000, console.log('Server is running')))
        .catch(error => { throw error })
```

This link comes from MongoDB Atlas, it allows us to connect to the database. And I also use the credentials held on `nodemon.json` to build the `uri`.

Next, we use again `mongoose` and pass as parameters the `uri` and some options to the `connect()` method. And when the operation is successfully finished, we start the server, otherwise, an error will be thrown.  

With that change, we have now connected the API to MongoDB. It's time now to test it with the GraphQL playground to see if the API works as expected. 

## Test the API with GraphiQL

To access the GraphQL playground, we have to start the server with the following command:

```shell
  yarn start
```

Now, if you browse to `http://localhost:3000/graphql`, you will be able to play with GraphiQL.

- Create a new article

To create an article, we have to send a query mutation.

![mutation](https://drive.google.com/uc?id=16zyPla66YVaUHUpmBjnFCD-XGsBQhSzC)

Seems like it works perfectly, the mutation creates a new article and returns it as expected. 

Now, let's try fetching the articles stored on MongoDB.

- Fetch the articles

As I said earlier, GraphQL allows us to fetch all or just the fields we need.

![query](https://drive.google.com/uc?id=1L2cZowOj9arHpQ6FMLbUUSvWd5Ryo_rX)

And here, I want to fetch for each article the title, body and, createdAt from the database. And, as you can see the response returned by MongoDB is the expected one. 

Great! We have now done building a GraphQL API from scratch with Node JS, Express, and MongoDB.

You can find the source code [here](https://github.com/ibrahima92/graphql-api)

Thanks for reading

| [TWITTER](https://twitter.com/ibrahima92_) | [BLOG](https://www.ibrahima-ndaw.com/) | [NEWSLETTER](https://ibrahima-ndaw.us5.list-manage.com/subscribe?u=8dedf5d07c7326802dd81a866&id=5d7bcd5b75) | [GITHUB](https://github.com/ibrahima92) | [LINKEDIN](https://www.linkedin.com/in/ibrahima-ndaw/) | [CODEPEN](https://codepen.io/ibrahima92) | [DEV](https://dev.to/ibrahima92) |
| -------------- | ------------------------------------------ | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------ | ---------------------------------------- | -------------------------------- |