DEV Community

vinodchauhan7
vinodchauhan7

Posted on • Updated on

MERN App using GraphQL via Apollo-Client,(React Hooks). (Part-1)

In this article, We will try to understand about graphQL, Apollo-client and how to implement both of this in our application.

NOTE : If you don't know about React & Node, then please first understand some basics of both the technologies. It will help me to make you understand about this article.

GitHub Link : MERN-app-using-graphql-apollo-client

What is GraphQL?

GraphQL is a powerful query language which allows to communicate data between client and server.It is more flexible and efficient approach than REST.

Alt Text

Certainly we not need GraphQL in every situation for data communication but in some scenarios graphql can be very optimizing approach. For instance, Lets' assume we need to build a Car Management System. In this application for learning purpose we have below possible cases :
1) A car can have multiple Owners.
2) A Owner can have multiple cars.

Considering above cases in mind, In REST approach we can have paramount 2 API's in our stack :

=> Endpoint for getting a single car.
API :: 'domainName/api/car/:id'
Response :: 'name,model,company,ownerId

=> Endpoint for getting Owner info.
API :: 'domainName/api/owner/:id'
Response :: 'name,age,gender,carIds

Lets' picture this what happen if we need information of a single car with Owner details and other cars he owned. In this case we need to do many api hits as per number of cars he/she owned. We might get trapped in performance issue if our application has large customer base. To handle this problem upto a great extent we have better approach for such type of scenarios. We can use graphql here.

A GraphQL approach for such type of scenarios can be :

{
   car(id:3){
    name
    model
    company
    Owner {
      name
      age
      cars{
        name
        company
      }
    }
   }

   //or
   car(id:3){
     name
     Owner{
       name
       gender
     }
   }
}

All this information will be collected by just hitting single API only one time. That is the power of graphql. So lets get started.

Alt Text

Server Side Implementation

First of all you need to download NODE latest version in your system. After installing it. You can use VSCode editor for development. Its free.

    Open terminal on VScode for ease.
  1) create folder MERNAPP
  2) cd MERNAPP
  3) create folder server
  4) Hit "npm init" command on terminal for creating package.json file
  5) And press "Enter" till it ends asking you question for creating it.

  //After creating package.json, Install the following packages in one go.

  6) npm install express express-graphql graphql lodash mongoose cors --save

  //After doing this step your package.json file look like this:

  //package.json
  {
  "name": "server",
  "version": "1.0.0",
  "description": "Server with Graphql & mongodb",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "vinod Chauhan",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "express-graphql": "^0.9.0",
    "graphql": "^14.5.4",
    "lodash": "^4.17.15",
    "mongoose": "^5.6.13"
  }
}

Don't worry about all these packages, I will give you proper knowledge why and when we need these packages.

a) Setup Express App

1) On VS Editor in 'server' folder, create a new file 'app.js'.

 //Get the express library from node_modules which we have just downloaded.
const express = require("express");

//Making const of express() into a variable (JS function first class Object).
const app = express();

//When our application starts, it will listen on port 4000
app.listen(4000, () => {
  console.log("Server is listening on port 4000");
});

To run this app.js first download 'nodemon' on your system globally so that you do not need to start/stop application again and again to see the changes.

   //Install nodemon package
   npm install nodemon -g

   //Run your app.js with nodemon
   nodemon app.js
   [nodemon] 1.19.1
   [nodemon] to restart at any time, enter `rs`
   [nodemon] watching: *.*
   [nodemon] starting `node app.js`
   Server is listening on port 4000

Open your fav. browser and entry localhost:4000, it will show blank page as of now.

If you have completed till this point then we will setup graphql in express in our nextstep.

b) GraphQL Setup in Express app

Our 'express-graphql' server will help express server to understand about graphql and let us do our work.

   //Get the express library from node_modules which we have just downloaded.
   const express = require("express");

   const graphqlHTTP = require("express-graphql");

   //Making const of express() into a variable (JS function first class Object).
   const app = express();

   /*We can use graphql on express server with middlewares, so that whenever
    we need graphql query from frontend, our express server can handle it
    smoothly.
    graphqlHTTP method let us do what we want to do if we have captured 
    '/graphql' middleware.
   */
   app.use("/graphql", graphqlHTTP({}));

   //When our application starts, it will listen on port 4000
   app.listen(4000, () => {
    console.log("Server is listening on port 4000");
   });

In browser type : localhost:4000/graphql
On running app.js, currently it will give us error which says :

{"errors":[{"message":"GraphQL middleware options must contain a schema."}]}

Don't worry our next step is only to setup Graphql Schema.

c) GraphQL Schema

In server folder, create 'schema' folder and create 'schema.js' in it.

**Schema file mainly has 3 major responsibilities to do.
1) Create types from 'GraphQLObjectType' Object.
2) Define relationship between types.
3) Define 'RootQueries' to let user enter into graph and use data.

As schema.js file has the major role at server end, so we will gradually complete it step by step.

 /// schema.js

const graphql = require("graphql"); //use graphql package

/*Getting GraphQLObjectType function from 'graphql' to define the (dataType) 
 structure of our queries and their model type.
*/
const {
  GraphQLObjectType,
  GraphQLID,
  GraphQLString,
  GraphQLInt,
  GraphQLSchema
} = graphql;

//Defining CarType with its fields.
const CarType = new GraphQLObjectType({
  name: "Car",
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    model: { type: GraphQLInt },
    company: { type: GraphQLString }
  })
});

//Defining RootQuery
const RootQuery = new GraphQLObjectType({
  name: "RootQueryType",
  fields: {
    // Fields here will be the query for frontends
    //We are defining a 'car' query which can take (car ID ) to search in DB.
    car: {
      type: CarType, //Defining model for car Query
      args: { id: { type: GraphQLID } },  //args field to extract argument came with car query, e.g : Id of the car object to extract its details.
      resolve(parent, args) {
        //code to get value  from DB
      } //resolve function
    } //car query ends here
  } //fields end here
});

//exporting 'GraphQLSchema with RootQuery' for GraphqlHTTP middleware.
module.exports = new GraphQLSchema({
  query: RootQuery
});


In above file schema.js, We have done below steps :
1) Imported 'graphql' to use graphql in express server.
2) Grab different Objects,dataTypes from graphql library.
3) Creating constant 'CarType' type with its fields() from GraphQLObjectType().
4) Creating rootQuery 'RootQuery' with endpoint query 'car' for GraphQLSchema function.
5) Exporting all above as GraphQLSchema with 'RootQuery' as an argument in it.

App.js Changes

 //Get the express library from node_modules which we have just downloaded.
const express = require("express");

const graphqlHTTP = require("express-graphql");

//Imports
const schema = require("./schema/schema");

//Making const of express() into a variable (JS function first class Object).
const app = express();

/*We can use graphql on express server with middlewares, so that whenever
    we need graphql query from frontend, our express server can handle it
    smoothly.
*/
app.use(
  "/graphql",
  graphqlHTTP({
    schema: schema
  })
);

//When our application starts, it will listen on port 4000
app.listen(4000, () => {
  console.log("Server is listening on port 4000");
});

Till now, our output on browser has changed but to some other error.

  {"errors":[{"message":"Must provide query string."}]}

//Dont worry guys we will remove this error too.

d) Defining resolve function

First of all, lets get dummy data to make our query working for now.

Put below code in schema.js file

const graphql = require("graphql"); //use graphql package

const _ = require("lodash");

/*Getting GraphQLObjectType function from 'graphql' to define the (dataType) 
 structure of our queries and their model type.
*/
const {
  GraphQLObjectType,
  GraphQLID,
  GraphQLString,
  GraphQLInt,
  GraphQLSchema
} = graphql;

const CarsArray = [
  { id: "1", name: "S-Class", model: "2019", company: "Mercedes" },
  { id: "2", name: "Continental GT", model: "2019", company: "Bentley" },
  { id: "3", name: "Phantom", model: "2019", company: "Rolls-Royce" },
  { id: "4", name: "Panamera", model: "2019", company: "Porsche" },
  { id: "5", name: "A8", model: "2019", company: "Audi" },
  { id: "6", name: "I-Pace", model: "2019", company: "Jaguar" }
];

//Defining CarType with its fields.
const CarType = new GraphQLObjectType({
  name: "Car",
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    model: { type: GraphQLInt },
    company: { type: GraphQLString }
  })
});

//Defining RootQuery
const RootQuery = new GraphQLObjectType({
  name: "RootQueryType",
  fields: {
    // Fields here will be the query for frontends
    //We are defining a 'car' query which can take (car ID ) to search in DB.
    car: {
      type: CarType, //Defining model for car Query
      args: { id: { type: GraphQLID } },
//args field to extract argument came with car query, e.g : Id of the car object to extract its details.
      resolve(parent, args) {
        //code to get value  from DB
        /**
         * With the help of lodash library(_), we are trying to find car with id from 'CarsArray'
         * and returning its required data to calling tool.
         */
        return _.find(CarsArray, { id: args.id });
      } //resolve function
    } //car query ends here
  } //fields end here
});

//exporting 'GraphQLSchema with RootQuery' for GraphqlHTTP middleware.
module.exports = new GraphQLSchema({
  query: RootQuery
});

Below steps we have done in schema.js :
1) Import 'lodash' library to ease our life.
2) Dummy 'CarsArray' with needed details.
3) Defining 'resolve' function of 'car' query.

e) Test our Queries.

To test our query first we need to do a little change in our app.js file.

app.use(
  "/graphql",
  graphqlHTTP({
    schema: schema,
    graphiql: true
  })
);

By Adding 'graphiql:true', we enabled a inbuilt awesome tool for testing our queries. Now open: localhost:4000/graphql
Alt Text

On left side of the window as can be seen on above picture, write a query with details you want by giving id of the car object.

Whoooaaaa! we have just made one car query in graphql. Congratulations if you are getting the result similar like me.

f) Defining OwnerType :

In schema.js, write the following code.

const graphql = require("graphql"); //use graphql package

const _ = require("lodash");

/*Getting GraphQLObjectType function from 'graphql' to define the (dataType) 
 structure of our queries and their model type.
*/
const {
  GraphQLObjectType,
  GraphQLID,
  GraphQLString,
  GraphQLInt,
  GraphQLSchema
} = graphql;

const CarsArray = [
  { id: "1", name: "S-Class", model: "2019", company: "Mercedes" },
  { id: "2", name: "Continental GT", model: "2019", company: "Bentley" },
  { id: "3", name: "Phantom", model: "2019", company: "Rolls-Royce" },
  { id: "4", name: "Panamera", model: "2019", company: "Porsche" },
  { id: "5", name: "A8", model: "2019", company: "Audi" },
  { id: "6", name: "I-Pace", model: "2019", company: "Jaguar" }
];

var OwnersArray = [
  { id: "1", name: "Vinod Chauhan", age: 27, gender: "male" },
  { id: "2", name: "John Dow", age: 46, gender: "male" },
  { id: "3", name: "Kristen", age: 30, gender: "female" },
  { id: "4", name: "Paris", age: 44, gender: "female" },
  { id: "5", name: "Sylvestor", age: 26, gender: "male" }
];

//Defining CarType with its fields.
const CarType = new GraphQLObjectType({
  name: "Car",
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    model: { type: GraphQLInt },
    company: { type: GraphQLString }
  })
});

//Defining CarType with its fields.
const OwnerType = new GraphQLObjectType({
  name: "Owner",
  fields: () => ({
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    age: { type: GraphQLInt },
    gender: { type: GraphQLString }
  })
});

//Defining RootQuery
const RootQuery = new GraphQLObjectType({
  name: "RootQueryType",
  fields: {
    // Fields here will be the query for frontends
    //We are defining a 'car' query which can take (car ID ) to search in DB.
    car: {
      type: CarType, //Defining model for car Query
      args: { id: { type: GraphQLID } }, //args field to extract
      // argument came with car query, e.g : Id of the car object to extract its details.
      resolve(parent, args) {
        //code to get value  from DB
        /**
         * With the help of lodash library(_), we are trying to find car with id from 'CarsArray'
         * and returning its required data to calling tool.
         */
        return _.find(CarsArray, { id: args.id });
      } //resolve function
    }, //car query ends here
    owner: {
      type: OwnerType,
      args: { id: { type: GraphQLID } },
      resolve(parent, args) {
        return _.find(OwnersArray, { id: args.id });
      }
    }
  } //fields end here
});

//exporting 'GraphQLSchema with RootQuery' for GraphqlHTTP middleware.
module.exports = new GraphQLSchema({
  query: RootQuery
});

On Refreshing browser or typing localhost:4000/graphql, look for owner query.

Alt Text

I will try to upload the part-2 as soon as possible. Till the bye.

Top comments (0)