5 hours GraphQL Tutorial with NodeJS and Express
Tags: tutorial, graphql, node
Published: 2021-03-18 08:26:14

This is a tutorial that will show you how to make a graphQL server using graphQL, NodeJS, Express, MongoDB. 

At the end of this tutorial. You should know how graphQL works, be able to set-up a basic server that has one endpoint but allow you to gather information from many different databases in a single cluster. 

## Why GraphQL? 
Normally, when you build APIs, you normally have many different endpoints for many different purposes. 

The result is when you need changes on the front-end, you have to go back to the back-end and fix the API to make it return the right data. It is easy for you to do this when you have 10 APIs, but when you have 40, 50+ APIs with different complicated data to return it makes things harder to maintain. 

![how grapql works](https://miro.medium.com/max/2000/1*feOd6UwyHF71rRmRtj_B7g.png)

This same problem happens to Facebook so they decide to invent graphQL. Now we have many more pros like : 
+ Smaller payload 
+ Fetching all data as one 
+ Pre-defined Schema ( How your data should look like, this helps you have better understanding of data-flow in your app )

We also have some cons as well : 
+ Learning Curve (Yes! You need to spend time learning it )
+ Not popular ( It is strong. But GraphQL just provides a new way to manage and write your APIs. It does feel life-changing but RESTful APIs work just fine in many cases. In fact, you may never need GraphQL if you don't do it in your day-to-day job as a developer ). 

Still, If you still like GraphQL and want to see how you can create a basic server that allows you to fetch data from graphQL. Then this tutorial is for you. 

# Tutorial : 

## Installing npm packages : 
_ Firstly, you need to choose a directory and run `npm init` to start the project. 
_ Then install these packages (we will need them later on ): 
+ cors ( workaround CORS problems ) 
+ dotenv ( using environment variables ) 
+ express ( express server ) 
+ express-graphql ( connecting express with graphql ) 
+ mongoose ( working with mongoDB ) 
+ nodemon  ( keep your server on watch mode ) 

Your packages.json should look like this (versions of packages are not important, you just need to install the latest) : 
![packages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p0qh053g9yawrk6z4dbo.PNG)

## SetUp a databases with MongoDB 
We also need free-tier databases hosted on [MongoD](https://www.mongodb.com/) so we can see if our API works. 

You should be able to go to MongoDB create an account if you don't have one and create a project. 

![MongoDB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sb1lbu20axwbmpynggc3.png)

When you have the project, choose `connect` > `connect using mongodb compass` and try to look for the string like this : 

`mongodb+srv://admin:${process.env.MONGO_DB_PASSWORD}@graphql.oumb8.mongodb.net/test`

The `admin` and `password` could be different but it is okay. That's on you. Now just save it somewhere we will need it later.

## SetUp Server ( app.js ) 
```javascript 
const express = require('express');
// grapqlHTTP allows us to use built-in interfaces to test our API
const { graphqlHTTP } = require('express-graphql');

// Using environment variable to store mongoDB databases password
const dotenv = require('dotenv');
dotenv.config();

// Connecting mongoose with databases
const mongoose = require('mongoose');
mongoose.connect(`mongodb+srv://admin:${process.env.MONGO_DB_PASSWORD}@graphql.oumb8.mongodb.net/test`,
{ 
    useNewUrlParser: true,
    useUnifiedTopology: true 
});
mongoose.connection.once('open', () => {
    console.log('Connected to databases');
});

// CORS 
const cors = require('cors');

// GrapQL Schema ( We haven't created it yet but we will soon )
const schema = require('./schema/schema');

const app = express();

app.use(cors());
app.use('/graphql', graphqlHTTP({
    graphiql: true,
    schema: schema
}));
const port = process.env.PORT || 5000;

app.listen(port, () => {
    console.log(`SERVER IS RUNNING ON PORT ${port}`);
});
```

Now I know this is long. But all of the things above are just boilerplate. So don't stress yourself too much. It always required a lot to setup a server even it is just a basic one. 

Also, create .env file like this to use your environment variables: 
```
MONGO_DB_PASSWORD=your-mongodb-password
```

## Create Mongoose Model 
Mongoose models are used to define how your data will be stored in mongoDB and also it is a way to connect to mongoDB to perform CRUD operations and more.

We will create 3 simple Mongoose Models : User, Hobby, Post 
User.js 
```javascript 
const mongoose = require('mongoose');
const UserSchema = mongoose.Schema({
    name: String,
    age: Number,
    job: String 
});
module.exports = mongoose.model('User', UserSchema);
```
 
Basically, we just create a UserSchema that will have name, age, job fields. Now we just need to do the same with Post and Hobby.

PostModel should have: comment, userId
HobbyModel should have: title, description, userId  
I believe you can do it on your own. It is the same as UserModel.

## Create GraphQL RootQuery and Mutation: 

#### 1.Definition about RootQuery and Mutation :  

**RootQuery** is where you can define what queries you want to build. Basically, all queries of your graphql API are stored in this RootQuery. So you can access it through a single endpoint. 

**Mutation** also just works like RootQuery but now it is used to store methods used to change your data instead of just read it.

Eventually, you will provide it to GraphQLSchema like this : 
```javascript 
const RootQuery = ...
const Mutation = ...

module.exports = new graphql.GraphQLSchema({
    query: RootQuery,
    mutation: Mutation
});
```

#### 2.Define types for RootQuery :

Just like we talked about one of the pros of RootQuery, we have pre-defined schema in GraphQL to help us to know what Queries from RootQuery will return. All of this is thank to `types` we will define in RootQuery. 

A type in GraphQL will have 2 basic fields: name, fields and in every field of fields we have 3 basic fields: type, args (optional), resolve.

Enough for talking, we will now create a UserType to see what is a type in GraphQL. 

Schema.js 
```javascript 
const graphql = require('graphql');
const User = require('../models/User');
const Post = require('../models/Post');
const Hobby = require('../models/Hobby');

const UserType = new graphql.GraphQLObjectType({
name: 'UserType', //We will use this `name` in RootQuery
fields: () => ({
        id: {type: graphql.GraphQLID},
        name: {type: graphql.GraphQLString},
        age: {type: graphql.GraphQLInt},
        job: {type: graphql.GraphQLString},
        posts: {
            type: graphql.GraphQLList(PostType),
            resolve(parent, args) {
                const postArrays = Post.find({
                    userId: parent.id
                });

                return postArrays;
            }
        },
        hobbies: {
            type: graphql.GraphQLList(HobbyTypes),
            resolve(parent, args) {
                const hobbiesArray = Hobby.find({
                    userId: parent.id 
                });

                return hobbiesArray;
            }
        }
})
});
```
I will explain. Here in this UserType when we query for users we will expect to see 6 fields in return for each user and that are: id, name, job, age, posts, hobbies.

In this case, posts and hobbies are more special mainly because they have their own databases on MongoDB. we will need to use `args` (optional) and `resolve(parent,args){}` to fetch them from their own databases. 

`args`: is where you will provide arguments that will be used in `resolve` method used to fetch data.

`resolve(parent, args){}`: is where you fetch your data and will have access to the parent element (User databases) and args that you provided earlier.

In `resolve(parent, args){}`, we just connect to our databases and doing Mongoose stuffs. This is not a tutorial about how to work with Mongoose so I guess you can figure this out. 

**Knowing that fields in GraphQL and Mongoose Models don't have to be exactly 100% like each other. Fields in GraphQL act like bridge between your databases where you can keep gathering information by jumping between Types while Model in Mongoose defines how your data will be stored in your databases. These two are different.**

You can do the same for HobbyType and PostType then come back here to check. 

```javascript 
const HobbyTypes = new graphql.GraphQLObjectType({
    name: 'HobbyType',
    description: "Model for HobbyType",
    fields:() => ({
        id: {type: graphql.GraphQLID},
        title: {type: graphql.GraphQLString},
        description: {type: graphql.GraphQLString},
        user: {
            type: UserType,
            resolve(parent, args) {
                const user = User.findById(parent.userId);

                return user;
            }
        }
    })
});

const PostType = new graphql.GraphQLObjectType({
    name: "PostType",
    description: "Model for PostType",
    fields: () => ({
        id: {type: graphql.GraphQLID},
        comment: {type: graphql.GraphQLString},
        user: {
            type: UserType,

            resolve(parent, args) {
                const user = User.findById(parent.userId);

                return user;
            }
        }
    })
});
```

In this situation, `user` field from `fields` acts like a bridge. You can gather userInfo then in userInfo you will have a post inside a postArray that contain info about the user and this user (same user) have a postArray and the cycle repeat. This is the power of GraphQL. **You can freely fetch data by jumping between databases as long as you define them in `fields`**.

#### 3.Building RootQuery :

If you know how to build Types like above, you will know how to build RootQuery. It's basically the same.

```javascript 
const RootQuery = new graphql.GraphQLObjectType({
    name: "RootQuery",
    fields: {
        user: {
            type: UserType,
            args: {
                id: {type: graphql.GraphQLID}
            },
            async resolve(parent, args) {
                const user = await User.findById(args.id);

                return user;
            }
        },
        users: {
            type: graphql.GraphQLList(UserType),
            async resolve(parent, args) {
                const users = await User.find();

                return users;
            }
        },
       
        hobby: {
            type: HobbyTypes,
            args: {
                id: {type: graphql.GraphQLID}
            },
            async resolve(parent, args) {
                const hobby = await Hobby.findById(args.id);

                return hobby;
            }
        },
        hobbies: {
            type: graphql.GraphQLList(HobbyTypes),
            async resolve(parent, args) {
                const hobbies = await Hobby.find();

                return hobbies;
            }
        },

        post: {
            type: PostType,
            args: {
                id: {type: graphql.GraphQLID}
            },
            async resolve(parent, args) {
                const post = await Post.findById({
                    id: args.id
                });

                return post;
            }
        },
        posts: {
            type: graphql.GraphQLList(PostType),
            async resolve(parent, args) {
                const posts = await Post.find();

                return posts;
            }
        }
    }
});
```

You will have 6 queries, but if you can understand how we build `user` query, you will understand the rest. 

`user` query will fetch a user using `id` provided in `args`. `user` has the `UserType` so when it returns a user, it will return 6 fields including `posts` and `hobbies` of this user. `posts` and `hobbies` are generated in UserType itself and not by we define `posts` and `hobbies` in RootQuery. These two are also different.  

That is it for RootQuery. By this time, if you have data stored in MongoDB you will be able to test your GraphQL API using localhost, on how to actually call your GraphQL API I recommend you watch [15 mins tutorial](https://www.youtube.com/watch?v=0ZJI4cBS4JM) since it really time-consuming for me to explain it here. 

#### 4.Building Mutation:

The same idea with RootQuery. I will only post one method for mutation so you can understand how can you write a Mutation for GraphQLSchema : 
```javascript
const Mutation = new graphql.GraphQLObjectType({
    name: 'Mutation',
    fields: () => ({
        createUser: {
            type: UserType,
            args: {
                name: {type:graphql.GraphQLNonNull(graphql.GraphQLString)},
                age: {type: graphql.GraphQLNonNull(graphql.GraphQLInt)},
                job: {type: graphql.GraphQLString}
            },
            async resolve(parent, args) {
                let user = new User({
                    name: args.name,
                    age: args.age,
                    job: args.job
                });
                const newUser = await user.save();
                return newUser;
            }
 })
})
```

You pass data you need to create a user to `args`. Then you create a new user in `resolve` and update it using Mongoose. As simple as that you can create a user through interfaces. 

Now you can create more methods like : fetch singleUser, updateUser, deleteUser, createHobby, createPost,... Anything really. After this you can check my github code link below to see if you are right or wrong. 

[Github Code Base](https://github.com/Tris-909/Graph-QL)

At this point you should be able to : 
+ Create a Type and define it in RootQuery 
+ Create methods in Mutation to change your data in your databases
+ Fetching and Mutating all the fields in databases successfully through your local-host.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3xrcumzt0eg5wpw703vr.png)

+ Be able to fetch infinity fields by jumping between two types in graphql like this : 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tql83f2n00la63xzip3z.png)

From what you learn from this tutorial. You can start building a server of any application you want as long as you know how your data will look like. If you don't like to build anything at least you can understand how GraphQL works in certain ways. 

--peace--