This article will teach you the concepts in GraphQL to enable you to perform queries and mutations on a data set. GraphQL is a query language and specifications for APIs that enable clients to request specific data, promoting efficiency and flexibility in data retrieval.
At the end of this article, you will know:
What GraphQL is
The problem GraphQL solves
The difference in using HTTP methods in REST API vrs query and mutation in GraphQL API to fetch and manipulate data.
How to define the shape of the data to query using Schema
How to query and mutate data on a GraphQL server using ApolloSandbox
In this tutorial, you will learn how to fetch and mutate using the local data of students and course array. The complete code snippet is on Github
Prerequisite
Familiarity with JavaScript
Familiarity with NodeJS
Introduction to GraphQL
GraphQL is a query language for your API, and a server-side runtime for executing queries using a type system you define for your data.
This means it provides a way to request and modify data from multiple data sources ( for instance, a REST API, SQLite database, and services) in a single query

GraphQL adopts the concept of a graph an approach of interconnecting different data to form a relationship. In modern applications, numerous data can be connected to form the app data graph. For instance, if you are building a blogging app that has resources (data) such as:
Posts
Authors
Comments
Followers
These entities can be connected to form a relationship. Whenever a developer wants to access a resource, he writes a query that specifies exactly what data he needs.
GraphQL is akin to visiting a restaurant, and telling the chef exactly what you want for your menu rather than accepting the provided menu.
For your menu, you can specify:
The type of protein (chicken, fish, tofu) to be served
The cooking method (grilled, baked, etc) to be used.
The sides ( salad, veggies, etc) to be added
And any specific sauces or toppings.
Similarly, with GraphQL, you can request specific fields from different parts of a data. The server then fulfills your request, giving you the exact data you need, without any unnecessary information.
There are two main components of GraphQL:
GraphQL Server
GraphQL Client
The GraphQL server is responsible for implementing the GraphQL API on the server side. Examples of GraphQL servers are:
Express
Apollo server
The GraphQL client allows apps to interact with the GraphQL server. It enables fetching and updating data in a declarative manner.
Examples of GraphQL clients are:
Relay
Apollo Client
What problem does GraphQL solve: data fetching with REST API vrs GraphQL
Generally, front-end developers display data on the UI of an app by sending requests to multiple endpoints.
For instance in building a blogging app. You will have a screen that displays the titles of the posts of a specific user. The same screen also displays the names of the last 3 followers of that user.
With a REST API, these resources may be on different endpoints. To fetch these resources, you may need to send different requests.
For instance, here are the steps to fetch and display the data in the blogging app using a REST API:
Fetch request to
/users/<id>endpoint to fetch the initial data for a specific user.Another request to the
/users/<id>/postsendpoint is to return all the posts and then filter out the title.Finally, a request to
/users/<id>/followersendpoint to return a list of the followers for the specified user.
In this scenario, you are making three requests to different endpoints to fetch the required data to build the UI.
This introduces two issues:
Over-fetching
Under-fetching
Overfetching is when a client downloads additional information than is required in the UI. For instance in the /users/<id>/posts/ endpoint, the data required is the title for each post. However, additional information may be returned such as:
id
post body,
likes, etc.
These will not be displayed in the UI, and can lead to:
Unnecessary transmission of data over the network.
Increased response times, and possibly impacting the performance of the client app.
Underfetching is when a client needs to make multiple requests to the server to gather all the necessary data for a particular view or operation. This happens when a specific endpoint doesn’t provide enough of the required information hence the client will have to make additional requests to fetch everything it needs.
Underfetching can lead to unnecessary network calls as numerous exchanges between the client and server increase the request time and network traffic.
GraphQL solves these issues of underfetching and overfetching by enabling clients to specify their exact data requirements in a *single query*. A "single" query means only one request is made to a GraphQL server to fetch specific data. That query will contain all the data required by the client.
Understanding the client and server roles
The GraphQL API is built on top of a GraphQL server. The server is the central point for receiving GraphQL queries. Once the query is received, it will be matched against a defined schema (you will learn more about schema as you proceed). The server retrieves the requested data by interacting with databases, microservices, or other data sources.
The GraphQL server consists of:
Schema: This represents the structure or shape of your data set.
Resolvers: They are functions that specify how to process specific GraphQL operations and contain the logic for fetching the requested data.
Data Sources: any available data source. Eg. MySQL, MongoDB, etc
The GraphQL Client enables apps to interact with a GraphQL API. You will describe the data your app needs in the GraphQL client, send queries and mutations to the server, and receive the response.
Examples of GraphQL clients are:
Apollo Client
Fetch QL
Relay
So far, you know what GraphQL is and the problem it solves. In the next section, you will learn how to build a GraphQL Server using the Apollo server.
Setting up a GraphQL Server
In this section, you will:
Build and run an Apollo Server
Define the GraphQL Schema that represents the structure of your data sets.
Let's get started
Step 1: Creating a new project
Create a directory and navigate into the directory
-
Inside the directory, initialize a Node.js project and set it up to use
ESmodulesnpm init --yes && npm pkg set type="module" -
Install
graphqlandapollo-serverdependencies using the command below:npm install @apollo/server graphql
Step 2: Create an instance of the Apollo server
Create an
index.jsfile inside the root directory. This will contain all the server requirements.-
Add the code below to the
index.jsfileimport { ApolloServer } from "@apollo/server"; import { startStandaloneServer } from "@apollo/server/standalone"; const server = new ApolloServer({ //typeDefs, //resolvers, }); const { url } = await startStandaloneServer({ server, listen: { port: 4000 }, }); console.log(`Server ready on port ${url}`);
In the code above:
The
new ApolloServer()constructor creates a new instance of the server. This accepts anobjectwith two properties: the schema definition and the set of resolvers.Next, you will pass the server instance to the
startStandaloneServerfunction. This will create an Express server, install the ApolloServer instance as middleware, and prepare your app to handle incoming requests.
Step 3: Define your GraphQL Schema and types
The GraphQL schema specifies the available data, its format (data types), and the operations(queries, mutation) that can be performed on it. When the query is initiated the data returned should match the structure defined in the schema.
Here is what you should know about a schema:
The schema is a collection of types (and the relationship between these types).
-
Every type you define can be categorized into:
- Scalar: This is similar to the primitive types in JavaScript (
String,Float,Int,Boolean,ID, etc) - Object: This represents the core items that can be fetched and what fields it has.
- Scalar: This is similar to the primitive types in JavaScript (
-
The schema also has two special types:
-
QueryandMutation: These detail what data you can request or mutate. You will learn more aboutqueriesandmutationstypes later.
-
Below is the syntax of an object type definition:
type EntityName {
#field name and the corresponding type
fieldName: scalarType
}
In this tutorial, clients can query from an array of students and course data. Hence, you will define the structure for the Course and Student as object type in the schema.
Here's an example of a Course object in a schema definition.
# Example of schema defintion
# This represents a Course object with fields that object has
type Course {
id:ID
name:String
cost:String
rating: Float
}
Let's examine the type above:
Courseis the name of the object type.The details between the
{and}are fields of theCoursetype. That meansid,name,cost, andratingare the only visible fields in any GraphQL query that works on theCourseobject.The
ID,StringandFloatare the scalar types. It means the returned data for the field should be of typeString,FloatorID
Now, let's create a schema.js file in the root directory, and define a variable called typeDefs to store all our types in the schema. Later, you will pass this typeDefs to the server.
#schema.js
export const typeDefs = `#graphql
# Your schema will go here
`;
export const typeDefs = `#graphql
# define the Course object
type Course {
id:ID
name:String
cost:String
rating: Float
}
# define the Student type
type Student {
id: ID
name: String
level: String
courses:[String]
}
`;
In the above, we have two objects in our schema: Course and Student each with its structure.
Understanding theQuery Type
Let's tell GraphQL what to retrieve when we query. The Query type indicates what you can fetch from the data source.
Here is what you need to know about the Query type:
It can be compared to the 'read' operations in a CRUD (Create, Read, Update, Delete) system.
In the
Querytype arefieldsthat acts as entry points into the rest of our schema.
The advantage of GraphQL is that you can fetch data from various resources using a single query ( Query type). However, with REST APIs different endpoints are required to fetch various resources (e.g api/students, api/courses).
Below is the syntax for a Query :
# This is the Root Query
type Query {
# within indicate the specify fields to query and what the expected results should be
field: returnedType
field: returnedType
}
On the front-end, we want to fetch an array of courses, students, and the details of a specific student
Here is how we will define them in our root Query
# Use the Root Query to define what you can fetch from the data source
type Query {
#get courses array
courses: [Course]
#get students array
students:[Student]
#Fetch a specific student by providing a student's ID as argument
student(id:ID!): Student
}
In the code above, clients will be able to execute a single query by specifying the courses and students fields.
courses: This returns an array of courses of typeCoursestudents: This returns an array of students of typeStudentstudent: This field accepts anidparameter and returns specific student details
Step 4: Define your data set
In the previous steps, we define the structure of our data and what we can query. Now, we will define the data itself.
Apollo Server can fetch data from any source you connect to ( for instance, SQLLite database, REST API, etc).
In this tutorial, you will use a local data source.
Create a
db.jsfile in your root directoryAdd the code below
let courses = [
{
id: "1",
name: "Web Development",
cost: "300",
rating: 4.5,
},
{
id: "2",
name: "Digital Marketting",
cost: "230",
rating: 3.0,
},
{
id: "3",
name: "Data Analytics",
cost: "345",
rating: 3.9,
},
{
id: "4",
name: "Cyber Security",
cost: "341",
rating: 3.1,
},
{
id: "5",
name: "Mobile Apps",
cost: "465",
rating: 2.1,
},
{
id: "6",
name: "Artificial Intelligence",
cost: "604",
rating: 5.0,
},
{
id: "7",
name: "Maching Learning",
cost: "345",
rating: 2.5,
},
{
id: "8",
name: "Dev Ops",
cost: "567",
rating: 2.6,
},
{
id: "9",
name: "Backend Development",
cost: "345",
rating: 3.1,
},
];
let students = [
{
id: "1",
name: "Emmanuel Smith",
level: "100",
courses: ["Web Development", "Dev Ops"],
},
{
id: "2",
name: "Robert Taylor",
level: "200",
courses: ["Backend Development", "Machine Learning"],
},
{
id: "3",
name: "Emly Lastone",
level: "100",
courses: ["Frontend Development"],
},
{
id: "4",
name: "Clement Sams",
level: "300",
courses: ["Mobile Apps"],
},
{
id: "5",
name: "Lius Gracias",
level: "100",
courses: ["Machine Learning", "Backend Development"],
},
{
id: "6",
name: "Jeniffer Baido",
level: "200",
courses: ["Data Science"],
},
{
id: "7",
name: "Natash Gamad",
level: "300",
courses: ["Cyber Security"],
},
{
id: "8",
name: "Paul Graham",
level: "100",
courses: ["Web Development"],
},
{
id: "9",
name: "Musti Madasd",
level: "300",
courses: ["Artiificial Inteligence"],
},
{
id: "10",
name: "Victor Bruce",
level: "200",
courses: ["Mobile Apps", "Backend Development"],
},
{
id: "11",
name: "Lilian Taylor",
level: "200",
courses: ["Web Development"],
},
{
id: "12",
name: "Smith Chef",
level: "100",
courses: ["Backend Development"],
},
];
export default { courses, students };
Step 5: Add the typeDefs to the server
The Apollo Server needs to know about the types defined in the schema.js.
In the index.js file:
import the
typeDefsspecified in theschema.jsPass the
typeDefsas an argument to thenew ApolloServer({})
#index.js
...
import { typeDefs } from "./schema";
const server = new ApolloServer({
typeDefs, #type defs passed as an argument
#resolvers will be passed here later
});
....
Next, we will define the logic for querying and mutating the data. To accomplish this, you will use resolvers
Step 6: Set up a resolver
Resolvers are functions that generate a response for every GraphQL query. A resolver's mission is to populate the data for a field in your schema. It connects schemawith the data sources, fetches the requested data, and populates the fields in the schema with that data. Resolvers have the same name as the field that it populates data for.
In the schema.js you have the Query type below:
#schema.js
type Query {
# specify fields to query and what the expected results should be
courses: [Course]
students:[Student]
}
You will define resolvers for the courses and students fields of the root Query type so that they always return an array of Course and Student when queried.
Add the code below to the index.js file
//index.js
const resolvers = {
Query: {
//resolver function for students field
students() {
//connect to the data source and return students data
return db.students;
},
// resolver function for courses field
courses() {
// connect to the data source and return courses data
return db.courses;
},
},
};
Here are the steps to define a resolver:
Define all the resolvers in a JavaScript object named
resolvers. This object is called the resolver mapThe
resolverwill have a key ofQuerywith an object valueWithin the object, you will define a function that connects to the data source performs the needed operation, and returns the specified data. The function name should be the same as the field to query
In the code above the students and courses resolver functions connect to the data in the db.js file, and return the students and courses data respectively.
Next, you will pass the resolvers object to the server:
...
// Pass schema definition and resolvers to the ApolloServer constructor
const server = new ApolloServer({
typeDefs,
resolvers,
});
...
Let's recap what we have done:
Created an Apollo server
Defined the schema
Defined the resolver
Connected the resolver to the data
Passed the resolver and schema to the Apollo server instance
In the next sections, you will start the server, query, and mutate data from the source.
Step 8: Start the server
Let's start the Apollo server by running the command below:
node index.js
// if you have nodemon install use the command
nodemon index.js
You should now see the following output at the bottom of your terminal:
🚀 Server ready at: http://localhost:4000/
Fetching resources from the server
Our server is up and running. Now, we need to fetch data from the source. To do that, you will execute GraphQL queries on the server. Queries are operations that fetch resources from the server.
Because we do not have a frontend app, we will use Apollo Sandbox to execute the query. It provides a quick way to test GraphQL endpoints.
Visit http://localhost:4000 in your browser to open the sandbox. The Sandbox includes the Apollo Studio Explorer, which enables you to build and run operations on the Apollo server.
The Sandbox UI displays:
A middle panel for writing and executing queries
A right panel for viewing responses to the query results
Tabs for schema exploration, search, and settings
A URL bar for connecting to other GraphQL servers (in the upper left)
Let's learn how to fetch data from the GraphQL server.
Below is the syntax for a query:
query Query_name{
someField
}
Now, Let's execute a query for the students and courses fields
Enter the
querykeywordType the name for your query ( e.g StudentQuery, CoursesQuery, etc)
Within the curly brackets (
{}), enter the name of the field to query as defined by the rootQueryin your schemaOpen another curly bracket (
{}) and specify the exact fields to query for the object
Because you are using the ApolloSandbox, you can enter these steps in the "Operations" panel and click the "blue" button in the upper right.
Now, type the code below into the Operations panel and click the blue button.
# executing a query
query CourseStudentsQuery{
courses {
# only get these fields from the query
name
cost
}
students {
# only get these fields from the query
name
courses
}
}
This action will:
connect to the server
call the resolver function for that field.
The resolver function connects to the data sources, performs the needed logic, and returns a response
The response is a JSON object containing only the specified fields.
The response will appear in the "Response" panel
Here is a screenshot of the operations and the response
One advantage of GraphQL is enabling clients to choose to query only for the fields they need from each object
In the query above, we requested only these fields for each query
courses:
nameandcostfieldsstudents:
nameandcoursesfields
Hence, only these fields will show in the response. Now, we have fetched the specified data for two different resources with a single query eliminating over-fetching.
Understanding GraphQL arguments: querying for a specific field
In our schema, we have defined the entry points for courses and students, but we still need a way to query for a specific student by its ID. To do that, we'll need to add another entry point to our schema.
An argument is a value you provide for a particular field in your query to help:
retrieve specific objects
filter through a set of objects
or even transform the field's returned value.
To define an argument:
Add parentheses after the field name. Eg. fieldName()
Inside the parentheses, write the name of the argument followed by a colon, then the type of that argument. Eg. (
id:ID). Separate multiple arguments with commas.
Below is the syntax
type Query {
fieldName(argName: argType)
}
In this tutorial, we want to query for a specific student by ID
Inside the Query type in schema.js:
Add the
studentfieldWithin the parenthesis specify the
idargument and its type (id: ID)Specify the returned object type
Student
type Query {
#initial queries remains
...
#Fetch a specific student by providing a student's ID as argument
student(id:ID!): Student
}
Next, in the index.js define a resolver for the student field that uses the provided ID to fetch details of the student.
//index.js
const resolvers = {
Query: {
...
//resolver for the student field to fetch a specific student
student(parent, args, contextValue, info) {
return db.students.find((student) => student.id === args.id);
},
},
};
A resolver can optionally accept four positional arguments: (parent, args, contextValue, info)
The
argsargument is an object that contains all GraphQL arguments provided for the field in the schema.Inside the body of the resolver function, we connect to the database and find a student with
idthat equals theargs.id
Because we have defined the resolver function for the student, we can query for a specific student with the ID.
Here is how the operation will look like in ApolloSandbox
In the "Variables" section, we have a variable
studentIdwith a value of 5In the "Operation" section is
Studentquery. It accepts the variable$studentIdwith a type ofID!The
$indicates a variable and the name after indicates the variable nameNext, we specify the field to query and pass in the variable
We then specify that we only want to return the
namefield for that query
You know how to fetch data from the server using the ApolloSandbox. Next, let's learn how to mutate data.
Mutating data on a GraphQL server
A mutation is an operation that allows you to insert new data or modify the existing data on the server. This is similar to POST, PUT, PATCH and DELETE requests in REST
To modify data, you use the Mutatation type.
Here is how to mutate data in our schema:
Start with the
typekeyword followed by the nameMutationInside the curly braces, specify the entry point. That is the field to mutate.
We recommend using a verb to describe the specific action followed by the data the mutation will act on. Eg.
addStudent,deleteStudent,createStudentPass the ID as an argument to the field to mutate
Specify the return type after the user makes the mutation.
Let's delete a student from the database. Here is how to do that
#schema.js
type Mutation{
#entry point to delete a student
deleteStudent(id: ID!): [Student]
}
In the code above:
We use
deleteStudent(id: ID!)field to indicate we want to delete a student's data by passing theidas an argument.Once the user has deleted a student, we return updated data that specifies an array of
Studentobject.
Next, you will define the resolver function for this mutation. Inside the index.js and in the resolver object, type the code below:
//index.js
const resolvers = {
//code here remains the same
...
//The Mutation object holds the resolver methods for all mutations
Mutation: {
//function to mutate data of a specified student
deleteStudent(_, args) {
db.students = db.students.filter((student) => student.id !== args.id);
return db.students;
},
},
};
In the code above:
We added a
Mutationproperty to theresolversobject to indicate the mutation of dataWe defined the
deleteStudent(_, args)method and passed theargsobject as parameterInside the method, we access the database and filter out all students whose id does not match the
args.idFinally, we returned the results.
Now, let's delete a student using the Sandbox.
Open a new tab and in the "Operation" section, add the following code below:
mutation DeleteMutation($deleteStudentId: ID!){
deleteStudent(id: $deleteStudentId) {
//Student object should have the following fields
id,
name,
level,
}
}
In the above:
We used the
mutationkey to indicate a mutation of dataDeleteMutationis the general name for the mutation. We pass the$deleteStudentIdvariable as a parameter. This will hold the value passed in the 'Variable" section of the ApolloSandboxThe
deleteStudent(id: $deleteStudentId)is the field defined in the schema. It accepts the ID we passed in the "Variable" sectionWithin the body of the
deleteStudentwe specify the fields theStudentobject should return after deleting
Finally, in the "Variable" section, indicate the ID of the student to delete and click on the "DeleteMutation" button. The specified student will be deleted and the response is displayed in the "Response" section.
Let's learn how to add a new student.
-
In the
Mutationtype, defineaddStudentfield. This field will accept astudentargument of typeStudentInputsand return aStudenttype
Add the code below the Mutation:
#schema
type Mutation{
# add addStudent field
addStudent(student: StudentInput!): Student
}
In the next line after the Mutation, define the StudentInput. This will contain all the fields the new student will have
#Mutating data
type Mutation{
deleteStudent(id: ID!): [Student]
addStudent(student: StudentInput!): Student
}
# data to pass to addStudent
input StudentInput{
name: String
level: String
courses:[String]
}
Next, go to the index.js and in the Mutation property of the resolvers object, we will define a addStudent resolver function that adds a new student to the data.
Add the code below the Mutation:
//index.js
const resolvers = {
Query: {
...
},
//The Mutation object holds the resolver methods for all mutations
Mutation: {
...
// this will add new student to the students data
addStudent(_, args) {
let student = {
...args.student,
id: Math.floor(Math.random() * 1000).toString(),
};
db.students.push(student);
return student;
},
},
}
In the code above:
Within the body of the
addStudentmethod, we have defined astudentobjectThe
args.studentcontains all the arguments we will pass to the method and theidwill generate a random IDThe
db.studentaccess the students' data source, and we push the newstudentobject to itFinally, we return the details of the student we created.
Here is the screenshot on how to add a new student in the sandbox:
The
AddMutationaccepts a$studentvariable of typeStudentInputThe
$studentis passed to theaddStudentfunctionIn the "Variable" section, we define the input data. This is a student object with
name,levelandcoursesproperties.
Summary
You now have the required knowledge to query and mutate data with GraphQL. In summary, GraphQL is a query language for API that provides a way to request and modify data from multiple data sources in a single query.
For further studies, check this comprehensive tutorial on ApolloGraphql










Top comments (0)