Getting Started With GraphQL and Apollo-Server
Tags: graphql, javascript, apolloserverexpress, tutorial
Published: 2022-06-26 10:36:19

GraphQL is a query language used to query and retrieve data. It is an alternative to REST and it gives the ability to describe our data as `types` since it is a strongly-typed language. We describe a schema and query for the exact data or portion of data we want.

Describe your data (Schema):
```js
 type Student {
   studentId: ID!
   name: String
   sex: Male
   courses: [Courses]
 }
```
Ask for what you want:
```js
 {
   student(name: "Ada"){
      name
      sex
   }
 }
```
Retrieve predictable result: 
```js
{
  student: {
    name: "Ada",
    sex: "Female"
   }
}
```

## Schema
We define a `schema` to represent the shape of our data and queries. A `schema` can be liken to a blueprint which describes the different types, queries and mutation allowed by our GraphQL server.The GraphQL engine validates any incoming request against our defined schema.

```js
  //example schema 
  type User {
    name: String!
    userId: ID!
    sex: String!
  }
```
In the above example we have a `User` type that consist of the `name`, `userId` and `sex`. fields. These fields resolves to a GraphQL built-in scalar type of `String`, `ID` and `String` respectively. 

Fields that resolve to a single object and cannot have sub-selection in the query is refer to as a scalar type. 

```js
  query {
      getUsers{
         name
         userId
         sex
      }
   }
```
The above query field `getUsers` resolves to a concrete data of scalar type of `String`, `String` and `String` for the `name`, `userId` and `sex` fields. These fields do not have a further sub-selection hence the name as scalar. The scalar type represent the leaves of the query.

```js
  query {
    allBooks{
      title #scalar type of string
      authors{ #non scalar it has a further sub-selection
        name
      }
   }
  }
```
The above example is a sample query with a field of `allBooks` which resolves to a scalar type of string for the `title` ( no sub-selection) and an `authors` fields which has a further selection of `name` of the author.  

Some examples of GraphQL scalar types are:

+ String: A UTF‐8 character sequence.
+ Int: A signed 32‐bit integer.
+ Float: A signed double-precision floating-point value.
+ Boolean: true or false.
+ ID : This is a unique identifier like a database key

## Query and Mutation types
A GraphQL server has a `Query` and a `Mutation` type. These are special types that define the entry point of every GraphQL query.

```js
  type Query {
     #all the fields of the query object are listed
    allStudents: [User]
    student(studentId: String): User
  }

type User {
  name: String!
  age: Int!
  sex: String!
  studentId: ID!
}
```
The above describe a GraphQL `User` type and queries that can be performed on the server. The field `allStudents` in the Query object resolves and returns a list of `User` object while the `student` returns a single `User` object. How does the GraphQL system know how to get the data for these fields? 

## Resolvers

A resolver is a user defined function that resolves the fields of a type with data. Looking at the `Query` object; it consists of two fields which are `allStudents` and `student`. We will need to define two resolvers function with the same name to pull out the data from our database or store.

```js
//resolvers
  Query{
    allStudents:() => {
     //this function should return a list of users
    },

    student:() => {
      //this should return a single user
    }
  }
```
Data can be retrieved from any data source inside a resolver function as GraphQL does not care about the database or source you are using. You only need to point it to the resolver function which it calls to retrieve the data.

## Apollo Server
Lets see an implementation of GraphQL using Apollo Server.  
We can build a production ready GraphQL server using Apollo Server. 

## Create a new NodeJS project
+ make a new directory by typing:
```js
  mkdir graphql_tutorial
```
+ navigate into the new directory by typing: 
```js
 cd graphql_tutorial
```
+ Navigate to your project directory and type the command
```js
 npm init --yes
```
The above assumes that NodeJS is installed on your machine.

+ create a new file named `index.js`

```js
 touch index.js
```
+ create another file named `data.js`
This will serve as the data store to query against.
```js
//data.js file content
 const StudentData = [
  {
    studentId: "1",
    name: "John Parson",
    sex: "Male",
    booksCollection: ["1", "2"]
  },
  {
    studentId: "2",
    name: "Pokemon Geshia",
    sex: "Female",
    booksCollection: ["1"]
  },
  {
    studentId: "3",
    name: "Samson Loken",
    sex: "Male",
    booksCollection: []
  },
];

const BooksData = [
  {
    bookId: "1",
    title: "The Law Of The Land",
    authors: [
      { authorId: "1", authorName: "John Mason" },
      { authorId: "2", authorName: "Favour Damen" },
    ],
  },

  {
    bookId: "2",
    title: "The Next day is Close",
    authors: [{ authorId: "1", authorName: "John Mason" }],
  },
];

const AuthorData = [
  {
    authorId: "1",
    name: "John Mason",
    sex: "Male",
    address: "East West Lane",
    books: ["1", "2"]
    
  },

  {
    authorId: "2",
    name: "Favour Damen",
    sex: "Female",
    address: "Best Avenue Drive Zone",
    books: ["1"]
  },
];
module.exports = { StudentData, AuthorData, BooksData}
```
Copy the above code block and dump inside the `data.js` file.

+ install dependencies 

```js
npm install apollo-server-express apollo-server-core express graphql
```
Run the above command on the terminal to install the packages needed to create the GraphQL server.

+ install nodemon to auto run our code

```js
 npm install --save-dev nodemon
```
+ open the package.json file and under the scripts section add a start script that will run the server.

```json
  {
  "name": "graphql_tutorial",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "apollo-server-core": "^3.9.0",
    "apollo-server-express": "^3.9.0",
    "express": "^4.18.1",
    "graphql": "^16.5.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.18"
  }
}
```

+ create two files inside the project namely `schema.js` and `resolver.js`

```js
  touch schema.js
  touch resolver.js
```

+ open the `index.js` file and put the content below inside.

```js
const { ApolloServer } = require('apollo-server-express');
const { ApolloServerPluginDrainHttpServer } = require('apollo-server-core');
const express =  require('express');
const http = require('http');
const resolver = require("./resolvers");
const schema = require("./schema");

async function startApolloServer(typeDefs, resolvers) {
  const app = express();
  const httpServer = http.createServer(app);
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    csrfPrevention: true,
    cache: 'bounded',
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
  });

  await server.start();
  server.applyMiddleware({ app });
  await new Promise(resolve => httpServer.listen({ port: 5000 }, resolve));
  console.log(`🚀 Server ready at http://localhost:5000${server.graphqlPath}`);
}
startApolloServer(schema, resolver);
```
At the top we required Apollo Server and other boiler plate code needed to run the server. Next, we defined an `async` function called `startApolloServer` which receives as an argument the `typeDefs` and `resolvers` (schema and resolvers). We are going to define the `schema` and `resolver` for our Graph and pass it as an argument to the `startApolloServer` function. The server is available on `http://localhost:5000/graphql`

## Creating the Schema
We are going to write our schema inside of the `schema.js` we created earlier. Our schema will consist of a `User` type and some queries and mutation.

```js
const { gql } =  require("apollo-server-express");

  module.exports = gql` 
   type Query {
      allUsers: [User]
      user(studentId: String):User
   }
   type User {
    studentId: ID!
    name: String!
    sex: String!
 }
`
```
The above schema describes a `User` type and two queries field of `allUsers` and `user`. At the top of the file we required `gql` from the `apollo-server-express`. The schema is written inside the template string of `gql`. The content of the file is exported for use using `module.exports`.

We will need to create a resolver function for the fields of `allUser` and `user`.

Open the file `resolver.js` which was created earlier and let's update it with our resolver function. 

```js
const { StudentData, BooksData, AuthorData } = require("./data");


module.exports = {
  Query: {
    allUsers: (parent, args, context, info) => {
      return StudentData;
    },
    user: (parent, args, context, info)) => {
       const user = StudentData.find(student => 
       student.studentId == args.studentId);
     return user;
    }
 
  },
```
A resolver accepts four positional arguments which are :
+ parent: 
+ args: 
+ context: 
+ info
You can read more about them in the documentation [Here](https://www.apollographql.com/docs/apollo-server/data/resolvers#resolver-arguments)

The above resolves retrieves data from our static data file `data.js`. The `allUsers` resolvers do not take any arguments and return all the list of students. If we are pulling data from a database, this is where we would have performed the queries to retrieve the data. The return value must conform to the schema described. 

Lets start and execute the query we just wrote:
Run the server:
At the terminal run:
```js
 npm run start
```
This will start up the server and you will see a message on the terminal : `🚀 Server ready at http://localhost:5000/graphql`

Open your browser and navigate to `http://localhost:5000/graphql`. You will be presented with a playground where you can test your queries and mutation. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6qt3d06rq24vjmxasi0o.png)

Click on the explorer tab to open a new window for you to write out your query. 

```js
query{
  allUsers {
    sex
    studentId
    name
  }
}
```
Paste the above query inside the tab and click on the Run button. The above query is executed against your server and the result is displayed.
```js
{
  "data": {
    "allUsers": [
      {
        "sex": "Male",
        "studentId": "1",
        "name": "John Parson"
      },
     <--- rest of the result -->
    ]
  }
}
```
Apollo server will return a data object with the result of the query. The result of the `allUsers` query is an array with the list of the user object (sex,studentId and name). If you look at the file `data.js` you will noticed that the `StudentData` also consist of a `booksCollection` field which is an array of strings. You had notice that the returned data does not contain this field. This is because the `booksCollection` field is not defined inside our schema.

Let's write a query to get a particular user. The `user` query requires we pass in a `studentId` which will be used to fetch the user data.

```js
 query($studentId: String){
  user(studentId: $studentId) {
     name
     studentId
  }
}
```
At the top we defined a `studentId` variable that is of type string and we passed the value of the variable to the `user` field.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ud8yt7vaj8eskfrvlbx9.png)

The variables are entered below the query tab in the circled location of the image above. Run the query and put in a studentId that existed in the data file. The query runs and we get our result. 

Let's introduce some new types into our schema. We want to add a `Book` and an `Author` type to the schema. We will want to be able to query for the books owned by a student. In the `StudentData` array in the `data.js` file, we have a `booksCollection` which is a list of books by `ID` owned by a user. 

Open the `schema.js` file and add the following code to it.

```js
type User {
   #previous fields above
    booksCollection: [Book] #added the field booksCollection
 }

 type Book {
    bookId: ID!
    title: String!
    authors: [Author] 
 }
 type Author {
    authorId: ID!
    name: String
    sex: String
    address: String
 }
```
We have added a `Book` type and an `Author` type to the schema as well as extend the `User` type by adding a `booksCollection` field. Looking at the new types added, we can notice that the `User` type has a field called `booksCollection` which resolves into a list of `Book`. 

The `Book` type has a field called `authors` that resolves into a list of `Author` type. We have to tell our GraphQL how to retrieve the values for these listed fields. That is where our resolvers come in, we will need to add functions that the GraphQL server calls when it encounters these fields so as to resolve them.

Open the file `resolver.js` and let's add the resolvers for these field. 

```js
{
//resolver.js
//rest of the resolvers file

 Book: {
    authors: (book) => {
      const authors = AuthorData.filter((author) => {
        return author.books.includes(book.bookId);
      });
      return authors;
    },
 },
User: {
    booksCollection: (parent) => {
        const student = StudentData.find(s => s.studentId == parent.studentId);
        const booksArray = student?.booksCollection;
        const collection = [];
        booksArray?.map((bookId) => {
            const book = BooksData.find(e => e.bookId == 
   bookId);
            collection.push(book);
        });
        return collection;
    }
  }
}
``` 
In the `Book` type we have a field called `authors` that we will need to write a function for so as to retrieve the authors of a book. Remember that a resolver function takes 4 positional arguments of which the first is refer to as the parent of the field, we are resolving. In the case of the `authors` resolver the parent is a `book` field that we want to resolve its list of `authors`. 

The `authors` resolver returns a list of authors from the `AuthorData` array where the `book.bookId` is included in the author.books property of the `AuthorData` array. It simply means, return all the list of authors that wrote the particular book. 

Same above also applies to the `booksCollection` field of the `User` type which is resolved by finding the `user` by the `studentId` of the parent. The parent is the `User` collection. (we could as well name parent as user ). Once the user is found, the books owned by the user is retrieved by `mapping` over the `student.booksCollection` array. For each item, in the array, we retrieve the corresponding book and save it into another array called `collection`. Once we are done with the loop, we return the `collection` array. 

Let's run another query to retrieve a particular user and their collection of books.

```js
  query($studentId: String){
  user(studentId: $studentId) {
     name
     studentId
     sex
     booksCollection {
       title
       authors {
         address
         sex
         authorId
         name
       }
     }
  }
}
```
Put a valid value for the `studentId`. The GraphQL server runs the query and makes use of the defined resolver above and returns a user with the complete list of all the books owned.

We have seen how to retrieve data using the query object of GraphQL. Now, let us see how to mutate or change data in our data store.

## Mutation
To change data in our server, we will add the `Mutation` type. This type has fields which when resolved performs some changes to the data store.

Open the `schema.js` file and lets add the `Mutation` type.

```js
#rest of the code above
type Mutation {
    saveUser(studentId: ID!, name: String!, sex: String!, 
    collection: [String]): UserDataSaved
}

interface MutationResponse  {
    code: String!
    success: Boolean!
    message: String!
 }

 type UserDataSaved implements MutationResponse {
    code: String!
    success: Boolean!
    message: String!
    user: User
 }
#rest of the code below
```

Add the above code after the `Query` type. Looking closely at the added code, you will notice that we added an `interface` defined which we call `MutationResponse`. An `interface` in GraphQL is similar to interfaces in other programming language. It is a contract that an implementing type must execute, meaning any type that implement this particular interface must define a `code`, `success` and `message` field. 

We defined a `UserDataSaved` type that implements the `MutationResponse`. To fulfil the interface contract we added the fields of the interface to the type and also added the `user` field which is the new user added. 

> When making a mutation, it is recommended to return the new object that was added or changed in the response. 

The `Mutation` type has a field called `saveUser` which accepts four variables and it resolves with the `UserDataSaved` type. Let us write the resolver function for the `saveUser`

Open the `resolver.js` file and create a `Mutation` object:

```js
   Mutation: {
    saveUser: (_, { studentId, name, sex, collection }) => {
      const newuser = {
        studentId,
        name,
        sex,
        collection
      };
      StudentData.push(newuser);
      return {
        code: "200",
        success: true,
        message: "New user record saved",
        user: newuser
      }
    },
  },
```
The `saveUser` resolver accepts as argument, the `studentID`,`name`, `sex` and `collection` as arguments. Remember that the second positional arguments of the resolver function is the `args` object that is passed to the resolver. We destructured the `args` object to retrieve the `studentId`, `name`, `sex` and `collection`. We created a `newUser` variable using these values and we push it into the `StudentData` array. we return an object which matches our schema as response to the mutation. 

Let add a new user via the Apollo playground.

```js
 mutation($studentId: ID!, $name: String!, $sex: String!, $collection: [String]){
  saveUser(studentId: $studentId, name: $name, sex: $sex, collection: $collection) {
    code
    message
    success
    user {
      name
      studentId
      booksCollection {
        authors {
          address
        }
      }
    }
  }
}
```
After running the query, we get a response back showing that the user has been added to our student list. Note that the added user is to persisted to file. We can query for our newly added user. 

We have just covered the basics of GraphQl, in the next post we will explore further and learn more about GraphQL. The code for this tutorial can be found [Here](https://github.com/jamiebones/graphql_tutorial.git)


Thanks for reading!