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
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
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.
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
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
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.
Let's start writing some code.
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.
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.
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.
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 });
}
}
},
};
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).
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,
});
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.
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
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.
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
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.
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.