Want to be More Productive? Checkout my Open Source Productivity Application

GraphQL For Beginners: Part #2 Hello Real World

In this tutorial we're going to setup our project, install all the dependencies and write some GraphQL code.

Step#1: Let's create a new Github Repository

Head over to https://github.com/new, Enter the name and description, check the box to initialize the repo with a README file, Select Node for .gitignore file and select MIT License as the license. and then click on Create Repository

Now you should see your newly created repository. In this case its https://github.com/dhruv-kumar-jha/graphql-for-beginners

Let's clone this repository locally, git clone https://github.com/dhruv-kumar-jha/graphql-for-beginners.git.

After cloning, lets move into the cloned directory and in terminal run the command yarn init or npm init -y this will initialize npm and create our package.json file with default options.

You can easily edit package.json file and make any/all the changes you want there.

We will be making use of Yarn dependency manager to manage all of our dependencies, You can find more info here https://yarnpkg.com/en/, However all the commands can very easily be replicated with npm

If you're having trouble creating repository or cloning it, you can go through this tutorial http://www.dhruvkumarjha.com/articles/writing-and-publishing-a-simple-javascript-module-to-npm

Step#2: Let's install all the required Packages/Modules

Now that the basic setup is done, We need to install few modules, Which will allow us to run our server, connect to database and make use of GraphQL

Thankfully it's lot easy., We will be installing these modules

express
This will run our server
mongoose
This will help us communicate with our MongoDB database
graphql
This will process and handle GraphQL stuff for us
express-graphql
This makes it lot easier to use GraphQL with our Express server
body-parser
We need this for parsing request body
cors
This will allow us to communicate with our server from different domains (Frontend in this case)
bluebird
We need a promise library for Mongoose, From what i have heard bluebird is lot better than the default javascript Promise

We will be installing more modules later on, But these are the ones we need to get started.

Lets install all these modules by running the command

yarn add --dev express mongoose graphql express-graphql body-parser cors bluebird

This will create a new node_modules directory and add the dependencies in our package.json file as well.

Step#3: Lets Create Directory Structure For Our Application

We will create files and directories making sure the structure of our application is good.

We will create a new directory app and place most of our code in that directory, We will create a new file server.js within our ROOT_DIRECTORY and write our server code there.

We will create few more directories and files named app/graphql, app/global/config/index.js, app/services/models, app/graphql/types, app/graphql/queries, app/graphql/mutations, app/graphql/resolvers, app/graphql/index.js

Our directory structure will look like this

Step#4: Let's Setup The Database

We're using MongoDB as our Databse for this application.

We can create our own server and install MongoDB there, But its lot better to use Database-as-a-service provider for this application.

We will use the Sandbox mode database provided by https://mlab.com/. If you don't already have an account, Create one and login.

After logging in, Open https://mlab.com/create, Select Single-node and then Standard Line -> Sandbox. For database name enter graphql and hit Create new MongoDB deployment

After creating the database, Open it by clicking on its name, and then goto Users tab, There click on Add database user

Enter any username and password you want there but make sure you save that information somewhere.

I decided to go with the username: admin and password MySuperS3cretPassw0rd, I will change this before publishing this article.

We're almost done with setting up database, Now copy the database connection string, It will look something like mongodb://<dbuser>:<dbpassword>@ds163940.mlab.com:63940/graphql, Replace the username and password there and we'll have our connection string which we can use in our application.

In this case it's mongodb://admin:MySuperS3cretPassw0rd@ds163940.mlab.com:63940/graphql

Step#5: What Will We Be Building?

This is a good questions, We will get started by building a User service that will allow us to manage all the users in our application.

For this tutorial all we want our User service to do is suppor the CRUD functionalities.

  • 1. Show all users
  • 2. Create new user
  • 3. View specific user
  • 4. Update existing user details
  • 5. Delete a user
Our user service will contain the following fields.
name
Full Name of the user
email
Email address of the user, must be unique
password
Users password, Must be hashed before storing in database
phone
Phone number of the user
status
Whether this user account is currently active or disabled
created_at
Date and time when this user account was created
updated_at
Date and time when this user account was last modified

Let's start writing some code.

Step#6: Let's create our User Model

This model will allow us to query our MongoDB database, We can use this model for storing and accessing our data from database.

Since we'll be hashing our users passwords, Lets install a new library bcrypt-nodejs that will allow us to easily hash our passwords.

From PROJECT_ROOT directory, open terminal and run the command yarn add --dev bcrypt-nodejs or npm install --save-dev bcrypt-nodejs

This will install the bcrypt-nodejs library for us.

Now, create a new file app/services/models/User.js and add this code

'use strict'; const mongoose = require('mongoose'); mongoose.Promise = require('bluebird'); const bcrypt = require('bcrypt-nodejs'); const hash_password = ( password ) => { let salt = bcrypt.genSaltSync(); // enter number of rounds, default: 10 let hash = bcrypt.hashSync( password, salt ); return hash; }; const UserSchema = mongoose.Schema( { name: { type: String, required: true }, email: { type: String, lowercase: true, required: true, unique: true, }, password: { type: String }, phone: { type: String }, status: { type: Number, default: 1 }, }, { timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }, collection: 'users', } ); UserSchema.methods.comparePassword = function(password) { if ( ! this.password ) { return false; } return bcrypt.compareSync( password, this.password ); }; UserSchema.pre('save', function(next) { // check if password is present and is modified. if ( this.password && this.isModified('password') ) { this.password = hash_password(this.password); } // do stuff next(); }); module.exports = mongoose.model( 'User', UserSchema );

All we're doing here is creating our User schema with all the fields and field types.

UserSchema.pre('save' method runs before saving the record in database, We're automatically hashing all the passwords before storing it in databse.

Step#7: Let's Write Our Resolver Methods

Resolver methods are the ones that will handle fetching, inserting and deleting the data.

Create a new file app/graphql/resolvers/User.js and add this code

'use strict'; const User = require('../../services/models/User'); class UserController { constructor(model) { this.model = User; } // this will find all the records in database and return it index() { return this.model.find() .sort('created_at') .exec() .then( records => { return records; }) .catch( error => { return error; }); } // this will find a single record based on id and return it. single( options ) { return this.model.findOne({ _id: options.id }) .exec() .then( record => { return record; }) .catch( error => { return error; }); } // this will insert a new record in database create(data) { const record = new this.model(data); return record.save() .then( (record) => { return record; }) .catch( (error) => { return error; }); } // this will update existing record in database update(data) { return this.model.findOne({ _id: data.id }) .exec() .then( (record) => { Object.keys(data).map( field => { record[field] = data[field]; }); return record.save() .then( updated => { return updated; }) .catch( (error) => { return error; }); }) .catch( (error) => { return error; }); } // this will remove the record from database. delete( options ) { this.model.findById( options.id ) .exec() .then( record => { record.remove(); return { status: true }; }) .catch( error => { return error; }); } }; const user_controller = new UserController(); module.exports = user_controller;

We have made use of Javascript Class syntax here. All we have done here is defined methods that are responsible for fetching and manipulating the data.

We will use these methods in our GraphQL Queries and Mutations.

Step#8: Time to Create Our First GraphQL Type [ User Type ]

Finally we're entering the GraphQL World., Let's write our first Type

Create a file named app/graphql/types/User.js and add this code in it

'use strict'; const GraphQL = require('graphql'); const { GraphQLObjectType, GraphQLString, GraphQLID, GraphQLInt, } = GraphQL; const UserType = new GraphQL.GraphQLObjectType({ name: 'User', description: 'User type for managing all the users in our application.', fields: () => ({ id: { type: GraphQLID, description: 'ID of the user, Generated automatically by MongoDB', }, name: { type: GraphQLString, description: 'Full name of the user', }, email: { type: GraphQLString, description: 'Email address of the user, must be valid and unique', }, phone: { type: GraphQLString, description: 'Phone number of the user', }, status: { type: GraphQLInt, description: 'Status of the user, whether active or disabled', }, created_at: { type: GraphQLString, description: 'Date and time when this users account was created', }, updated_at: { type: GraphQLString, description: 'Date and time when this users account was last updated', } }) }); module.exports = UserType;

Just like our User Model, We're specifying all the fields our user type/object contains, Field type and also field description.

Although description is optional, I highly recommend adding it as this is used in generating the documentation which we will later see.

Step#9: Let's Create Our First GraphQL Query File [ User Queries ]

These GraphQL queries will allow us to view all the data in our database.

Create a file named app/graphql/queries/User.js and add this code in it

'use strict'; const GraphQL = require('graphql'); const { GraphQLList, GraphQLID, GraphQLNonNull, } = GraphQL; // import the user type we created const UserType = require('../types/User'); // import the user resolver we created const UserResolver = require('../resolvers/User'); module.exports = { index() { return { type: new GraphQLList(UserType), description: 'This will return all the users present in the database', resolve(parent, args, context, info) { return UserResolver.index({}); } } }, single() { return { type: UserType, description: 'This will return data of a single users based on the id provided', args: { id: { type: new GraphQLNonNull(GraphQLID), description: 'Please enter user id', } }, resolve(parent, args, context, info) { return UserResolver.single({ id: args.id }); } } }, };

Step#10: Let's Create Our First GraphQL Mutation File [ User Mutations ]

These mutations will allow us to manipulate the user data in Database.

Create a file named app/graphql/mutations/User.js and add this code in it

'use strict'; const GraphQL = require('graphql'); const { GraphQLNonNull, GraphQLString, GraphQLInt, GraphQLID, } = GraphQL; // lets import our user type const UserType = require('../types/User'); // lets import our user resolver const UserResolver = require('../resolvers/User'); module.exports = { create() { return { type: UserType, description: 'Add new User', args: { name: { type: new GraphQLNonNull(GraphQLString), description: 'Enter users full name, Cannot be left empty', }, email: { type: new GraphQLNonNull(GraphQLString), description: 'Enter users email address, Must be valid and unique', }, password: { type: new GraphQLNonNull(GraphQLString), description: 'Enter users password, will be automatically hashed', }, phone: { type: GraphQLString, description: 'Enter users phone number', }, status: { type: GraphQLInt, description: 'Enters users status, by default its set to active. 1: active, 2: disabled', }, }, resolve(parent, fields) { return UserResolver.create(fields); } } }, update() { return { type: UserType, description: 'Update user details', args: { id: { type: new GraphQLNonNull(GraphQLID), description: 'Enter user id', }, name: { type: GraphQLString, description: 'Enter users full name, Cannot be left empty', }, email: { type: GraphQLString, description: 'Enter users email address, Must be valid and unique', }, password: { type: GraphQLString, description: 'Enter users password, will be automatically hashed', }, phone: { type: GraphQLString, description: 'Enter users phone number', }, status: { type: GraphQLInt, description: 'Enters users status. 1: active, 2: disabled', }, }, resolve(parent, fields) { return UserResolver.update(fields); } } }, delete() { return { type: UserType, description: 'Delete existing USer', args: { id: { type: new GraphQLNonNull(GraphQLID), description: 'Enter user id', }, }, resolve(parent, fields) { return UserResolver.delete(fields); } } }, };

Things are looking good., Now all we have to do is create GraphQL Schema and then tie up everything with our server (which we have't created yet).

Step#11: Let's Create The GraphQL Schema File

This file will contain all of our queries and mutations we have defined and will be used by express-graphql.

Create a file named app/graphql/index.js and add this code in it

'use strict'; const GraphQL = require('graphql'); const { GraphQLObjectType, GraphQLSchema, } = GraphQL; // import the user query file we created const UserQuery = require('./queries/User'); // import the user mutation file we created const UserMutation = require('./mutations/User'); // lets define our root query const RootQuery = new GraphQLObjectType({ name: 'RootQueryType', description: 'This is the default root query provided by the backend', fields: { users: UserQuery.index(), user: UserQuery.single(), }, }); // lets define our root mutation const RootMutation = new GraphQLObjectType({ name: 'Mutation', description: 'Default mutation provided by the backend APIs', fields: { addUser: UserMutation.create(), updateUser: UserMutation.update(), deleteUser: UserMutation.delete(), }, }); // export the schema module.exports = new GraphQLSchema({ query: RootQuery, mutation: RootMutation, });

Step#12: Creating and Starting our Express GraphQL Server

Let's create our server see if everything works.

First, create/open a file named app/global/config/index.js and add this code in it.

'use strict'; module.exports = { server: { PORT: process.env.PORT || 1221, }, database: { HOST: process.env.MONGODB || 'mongodb://admin:MySuperS3cretPassw0rd@ds163940.mlab.com:63940/graphql', }, };

Here we're just setting the PORT and DATABASE connection information, We got the database connection info in Step#4., You can set the port value to anything you want.

process.env.PORT and process.env.MONGODB All this does is check if a variable named PORT and MONGODB is defined in the environment, If yes our application uses the values specified in the environment, If not it uses the value we have specified in the config file.

This alllows us to keep our Sensitive Information secure.

Now Create a file named server.js in our ROOT_DIRECTORY and add this code in it.

'use strict'; const express = require('express'); const body_parser = require('body-parser'); const cors = require('cors'); const config = require('./app/global/config'); const mongoose = require('mongoose'); const expressGraphQL = require('express-graphql'); // let's import the schema file we just created const GraphQLSchema = require('./app/graphql'); mongoose.Promise = require('bluebird'); mongoose.connect( config.database.HOST ); const app = express(); app.set( 'port', config.server.PORT ); app.disable('x-powered-by'); app.use( cors({ optionsSuccessStatus: 200 }) ); app.use( body_parser.json({ limit: '50mb' }) ); app.use( body_parser.urlencoded({ limit: '50mb', extended: true }) ); // here we specify where we want our GraphQL server to be accessible at, For now /graphql seems fine. // graphiql is the GUI we can use play around with our GraphQL server, Lets enable this for now, Disable this in the production. app.use( '/graphql', expressGraphQL( () => { return { graphiql: true, schema: GraphQLSchema, } }) ); // our default route. app.get( '/', (req, res) => { res.json({ code: 200, message: 'Hello World' }); }); // start the server app.listen( app.get('port'), () => { const port = app.get('port'); console.log('GraphQL Server Running at http://127.0.0.1:' + port ); } );

Now let's edit our package.json file and in scripts object add this code/line

"scripts": { "start": "node server.js" },

This will make sure when we run yarn start or npm run start node will run our server.js file.

Now cross your fingers and run yarn start in terminal.

You should see this message GraphQL Server Running at http://127.0.0.1:1221 unless you have changed it.

Now open http://127.0.0.1:1221/graphql and you should see a User Interface where we can play with GraphQL

Step#13: Playing with GraphiQL and Testing our GraphQL Queries and Mutations

From the GraphiQL UI lets create some users and try to access+update+delete their details.

Let's create a new user first, The syntax to execute a mutation in GraphiQL UI is

mutation { addUser (name: "John Doe", email: "john.doe@gmail.com", password: "Passw0rd") { id name email } }

Following the same syntax, Add few more users.

You can easily add phone number and other options details by providing it in addUser (name: "John Doe", email: "john.doe@gmail.com", password: "Passw0rd")

Example: addUser (name: "John Doe", email: "john.doe@gmail.com", password: "Passw0rd", phone: "124567890", status: 1)

Now to view all users in the database we execute this query

{ users { id name email phone } }

To view a Single user, we do this

{ user(id: "58f7642bb6520d0ba8c2f4af") { id name email phone status created_at updated_at } }

Replace the id here user(id: "58f7642bb6520d0ba8c2f4af") with an actual user id from your own database otherwise you will see an error.

To Update user details we run this mutation

mutation { updateUser(id: "58f7623cb6520d0ba8c2f4ad", phone: "9876543210") { id name email phone status created_at updated_at } }

You can specify the fields and values you want to update and don't forget to change the user id.

Now, Let's try deleting a user

mutation { deleteUser(id: "58f76405b6520d0ba8c2f4ae") { id status } }

Although the user is deleted, We're not seeing the id and status we asked for because we're not returning it and also i think i scrweed up somewhere, Anyways do let me know if you read this line since i don't know if any one will make it till the end.

Step#14: Let's Commit Our Code

Let's commit and push our code to Github.

Open terminal and run the command git status to see the status

Let's add all the files we created, git add -A

Now lets commit our changes, git commit -m "Added Users Service"

Finally, Lets push our code to Github git push

You can see the updated code in your repository, In this case https://github.com/dhruv-kumar-jha/graphql-for-beginners

EDIT:

Since we will be hosting our app on Heroku, We need all the dependencies to be in dependencies object instead of in devDependencies

So lets edit package.json file and rename our devDependencies or dependencies

Now follow the previous steps, Commit the changes and push it to Github.

Step#15: Let's Host Our Application On Heroku

Now that we have a working application, Let's host it on Heroku.

Open https://www.heroku.com/ or Create a new Account if you don't already have one, If you have an account Login.

After logging in, Goto https://dashboard.heroku.com/new?org=personal-apps and enter Application Name and then click on Create App

After creating the project, Goto Settings tab and click on Reveal Config Vars button.

Now create a variable with KEY: MONGODB and value: Whatever your MongoDB connection string is and click on the Add button. This is only required if you dont want to include the connection information in the application config file.

Now click on Deploy tab, In Deployment method select Github (if you haven't connected your Github account, please do so.)

After connecting, Search for the repository using its name and once you see it click on connect button

Afetr clicking on connect, New sections will appear, Find Manual deploy section and click on Deploy Branch button.

Wait for Heroku to deploy your application, Once done a View button will appear, Clicking on it will open the app in new tab

In this case the app url is https://graphql-for-beginners.herokuapp.com/

Open https://graphql-for-beginners.herokuapp.com/graphql to access our GraphiQL User Interface.

Hope you had fun reading the article.