Table of contents
Introduction
APIs (Application Programming Interfaces) have become essential to modern web apps in today's software development world. APIs allow developers to access and interact with data from different sources, making building apps that rely on data from multiple services easier. GraphQL has gained significant popularity as a powerful and flexible query language. It is an open-source query language developed by Meta that provides a more efficient approach to querying APIs.
If you are new to GraphQL and wondering how to write your first Query, you have come to the right place.
This guide covers the following:
An overview of GraphQL and its differences from traditional REST(Representational State Transfer) APIs
Introduction to writing your first GraphQL Query
Understanding the syntax of GraphQL queries
How to consume a GraphQL API in a React.js web app
Exploring everyday use cases for querying APIs with GraphQL
Upon completing this guide, you must have learned how to write a GraphQL query and be ready to build powerful web apps with GraphQL-powered APIs. So, let us get started!
Prerequisites
Basic knowledge of JavaScript
Basic knowledge of Node.js and its syntax
Any code editor you are comfortable using
What is GraphQL?
GraphQL is a query language that describes an API request. It is unrelated to databases. Query Languages are not specific to databases. They are programming languages used to get data from a database or an API. GraphQL is not a technology, library, or database; it is a query language that exists as a layer between an app’s front and backend. GraphQL acts as a middle-man layer between the front and backend, so either can not communicate directly. GraphQL is an alternative to REST APIs. With a REST API, we must make individual requests with different HTTP methods to a database to retrieve data, but with GraphQL, all of the requests must be made to a single endpoint.
Differences between GraphQL and REST APIs
GraphQL and REST APIs are two popular methods of retrieving data from an API or a database. While REST APIs have existed longer, GraphQL has recently gained significant popularity. Both technologies have strengths and weaknesses, and knowing which one to use for specific use cases is essential. Let us go over some of their differences:
Endpoints: With a standard REST API, we must make individual requests with different methods to get the appropriate data. With GraphQL, we only have to request a single endpoint without worrying about making requests to different endpoints.
Data Fetching: GraphQL allows the frontend to specify what data they need, and the server returns only that data. It helps to reduce network usage and make data fetching faster. With REST APIs, the server sends back all the data associated with an endpoint requested from the frontend, sometimes including irrelevant data and slow network optimization.
Error Handling: With REST APIs, errors are returned as HTTP status codes, which must be parsed and handled correctly in the frontend. Unlike GraphQL, errors are returned as part of the response data, making it easier for the frontend to handle errors correctly and provide more precise and concise error messages.
Documentation: One distinct difference between GraphQL and REST APIs is their approach to documentation. REST APIs often rely on external documentation to explain each endpoint and its associated parameters. On the other hand, GraphQL is self-documented as its available types and fields are already defined, making it easier to understand how to query the API without the need for external documentation.
It is advisable to choose either of the two methods for a specific use case, as both are popular methods of fetching data with a large community, making them easier to use.
GraphQL Terminologies: Types, Schema, Queries, Mutation, and Resolvers
It is essential to understand the basic terminologies of GraphQL, as it helps to simplify the complex concepts surrounding the language. By the end of this section, we expect to have a clear understanding of the concepts. These concepts lay the foundation for using a GraphQL API.
Types
One of the fundamental concepts in GraphQL is types. Types in GraphQL refer to the structure of data that can be queried from an API. We must always define data types like strictly-typed languages. Using GraphQL types, our API provides a more structured and consistent way of querying data. In GraphQL, there are three main types:
scalar types
object types
enumeration types
Scalar Types
Scalar types are primitive data types used to represent values. These types are namely: IDs,
strings
, integers
, floats
, and booleans
They are not types composed of fields or sub-fields.
Object Types
Object types in GraphQL are a set of fields, each with its scalar type. They represent complex data types that can have subfields and child types, each with its scalar type.
Example of an Object Type
type User {
id: ID!
name: String!
email: String!
age: Int
}
In this example, we have an object type called: User
. The User
type represents a particular user in our app and has fields for their id
, name
, email
, and age
If we notice, there is an exclamation mark (!)
after some data types. It indicates the fields are required and cannot be null. GraphQL accepts either a value or null.
Enumeration Types
Enumeration types in GraphQL are also referred to as enums
. They define a finite set of constant values used as a type for a field, ensuring only valid values are accepted for that field. Once a enum
has been defined, the values cannot be changed, ensuring they remain constant.
Example of Enums
enum Country {
CANADA
FRANCE
SPAIN
ENGLAND
}
In the example, we have defined a enum
called Country with four values: CANADA, FRANCE, SPAIN, and ENGLAND. This enum can be included as a type for an object field such as:
type User {
id: ID!
country: Country!
}
In the User
object, we have specified a type with a country field of type Color
. Using an enum type like the one we defined ensures only valid country values are accepted for the country field, making the API more consistent and accurate.
In addition to scalar, object, and enumeration types, GraphQL also supports unions and interfaces. Unions and interfaces enable the creation of abstract types representing multiple object types.
Schema
A schema in GraphQL is unrelated to a database schema. It is a schema that defines all of the data inside the API. Every GraphQL schema has a root object type called Query
. This root type contains all the queries we want to make from the frontend to request data from the API. Every GraphQL API must have a schema. A schema also defines the relationships between objects and any available queries or mutations.
Here is an example of a schema in GraphQL, using an example from the Countries GraphQL API:
type Query {
country(code: ID!): Country
language(code: ID!): Language
}
type Country {
code: ID!
continent: Continent!
currency: String
name: String!
native: String!
phone: String!
}
type Language {
code: ID!
name: String!
native: String!
rtl: Boolean!
}
In this example, we defined a schema with two object types, Country
and Language
, and two query fields, country and language.
The country query takes an argument code of type ID
and returns a single Country
object with fields code, continent, currency, name, native, and phone.
The language query takes an argument code of type ID and returns a single Language object with fields. code
, name
, native
, and rtl
.
By defining these object types and queries, the frontend can query the API for specific countries or languages and receive a standard response, including the specified fields.
Queries and Mutations
In GraphQL, there are two types of operations: queries and mutations. They are the primary way to request data from an API, whether for querying or changing the data. With GraphQL, we do not need to specify what HTTP method request we are making from the frontend; we need to specify if we are trying to make a query or a mutation.
Queries
A query is a request for data that specifies precisely what data the frontend wants and how it must be structured. All the data the frontend needs from the API or database is written inside the query.
Queries in GraphQL consist of one or more fields, each with its name and set of arguments that specify how the field must be queried. Fields can be scalar types, such as strings, integers, or object types, with subfields and child types.
The GET
method in REST API is synonymous with a Query operation in GraphQL.
Example of a query
query {
country(code: "US") {
code
name
phone
}
}
In this example, we request data for a GraphQL Country API country with the US
code. We specified the country's code
, name
, and phone
fields to be returned from the API. By specifying the fields we want in the Query, we can reduce the amount of data the API returns, resulting in more efficient network usage.
Mutation
Mutation is the second type of operation in GraphQL. A mutation is a type of operation used to mutate, alter, or change the data on the server side. Mutations can create, update, or delete data and are defined in the schema, object types, and queries.
While queries are used to retrieve data, mutations are used to make changes to that data. The REST API's CREATE
, UPDATE
, and DELETE
methods are synonymous with a Mutation operation in GraphQL.
For example, a mutation can create a new user account or update an existing one. Mutations typically take input arguments that define the changes to be made and return values that indicate the success or failure of the mutation.
An example of a mutation operation that creates a user
mutation {
createUser(input: {
name: "John Doe",
email: "john.doe@example.com",
password: "secret123456"
}) {
id
name
email
}
}
In this example, the createUser
mutation takes a single argument called input
, an object type containing the new user's name, email, and password fields. The mutation returns the newly created user's id
, name
, and email
fields.
An example of a mutation operation that updates a user
mutation {
updateUser(id: 111, name: "John Doe") {
id
name
email
}
}
In this example, the updateUser
mutation takes two arguments: id
, which identifies the user to be updated, and name
, which is the new name for the user. The mutation returns the user's updated id
, name
, and email
fields.
An example of a mutation operation that deletes a user
mutation {
deleteUser(id: 123) {
id
name
email
}
}
In this example, the deleteUser
mutation takes a single argument called id
, which is the identifier of the user to be deleted. The mutation returns the deleted user's id
, name
, and email
fields.
Resolvers
A resolver is a function that retrieves data for a particular field in a query or mutation. It triggers the request sent from the frontend and fetches data from the API based on the type of operation specified.
Each field in a GraphQL schema must have a corresponding resolver that returns the data for that field. Resolvers can fetch data from a database, call an API, or perform any other operation needed to retrieve the data. Resolvers are responsible for resolving the entire query,
Example of a Resolver in GraphQL
const resolvers = {
Query: {
user: (_, { id }) => {
return users.find(user => user.id === id);
}
},
User: {
name: (user) => user.name
}
};
In this example, the user
resolver function takes an id argument and returns the user object from the users
array, which matches the given ID. The User
resolver object defines a resolver for the name field, which returns the value of the name property on the user
object.
How to write and run a GraphQL query using the GraphQL Playground
To use GraphQL to fetch data from an API, the API must support the GraphQL fetching operation. It has to be a REST API that communicates with the GraphQL layer or a GraphQL API built using GraphQL.
GraphQL Playground is a tool for testing GraphQL APIs. It provides an interactive environment for executing queries, exploring the schema, and debugging errors. To test a GraphQL API in the Playground, we must provide the endpoint URL and write queries in the left-hand panel.
Other GraphQL clients offer similar functionality for testing and interacting with a GraphQL API, but this article places greater emphasis on using GraphQL Playground. To test our queries, we will work with the StarWars API.
To start, Open the GraphQL Playground by navigating to the Star Wars GraphQL Playground URL here.
Explore the API Schema:
Once the playground is loaded, you can find the API schema on the bottom left-hand side of the screen. Click the schema icon to explore the available types, queries, and mutations to use with the API.
Writing our Query:
We must implement our Queries operation knowledge to request the Star Wars API. From the schema defined, in the root query, there are different queries we can make requests to. Let us request the allPeople
query.
query {
allPeople {
people {
name
birthYear
species {
name
}
}
}
}
This GraphQL Query fetches a list of people and their details, including name, birth year, and species they belong to. The Query requests for all people and uses the allPeople
field to retrieve an object containing a list of people. The people field then returns an array of objects containing each person's name
, birthYear
, and species
details. The species field within the people field is a nested query retrieving the species names for each person.
In this example, we use the allPeople
field to fetch a list of people and their details. The people
field then returns an array of objects containing each person's name
, birthYear
, and species
details. After writing our Query, we must click the "Play" button to execute our Query.
Viewing the response:
After executing the query, the results are shown in the "Response" section of the screen. The response to the data is in this format:
{
"data": {
"allPeople": {
"people": [
{
"name": "Luke Skywalker",
"birthYear": "19BBY",
"species": null
},
{
"name": "C-3PO",
"birthYear": "112BBY",
"species": {
"name": "Droid"
}
},
{
"name": "R2-D2",
"birthYear": "33BBY",
"species": {
"name": "Droid"
}
},
{
"name": "Darth Vader",
"birthYear": "41.9BBY",
"species": null
},
{
"name": "Leia Organa",
"birthYear": "19BBY",
"species": null
},
{
"name": "Owen Lars",
"birthYear": "52BBY",
"species": null
},
{
"name": "Beru Whitesun lars",
"birthYear": "47BBY",
"species": null
},
}
]
}
}
}
The response data also provides us with an option to view any errors or warnings which may have occurred during the execution of our Query.
How to fetch a GraphQL API in a React.Js app
React.js is a popular frontend JavaScript library for building scalable and efficient user interfaces. It lets us create reusable UI components and declaratively define how they may behave in response to user interactions or data changes.
React.js helps build apps that let us consume data from a GraphQL API because it efficiently renders data from the API to components. React components can send queries to a GraphQL server using GraphQL client libraries like Apollo Client.
Apollo Client is a comprehensive JavaScript state-management library that enables us to consume and interact with GraphQL APIs in our app easily.
It provides a range of valuable features, such as caching, fetching, and modifying real-time data, and lets us easily integrate our GraphQL API with our frontend app.
Setting up our development environment
Our first step is to set up a development environment where we can start building our web app.
Run the following command via the terminal. I am using npm
it as my package manager, but you can use your preferred package manager's commands instead:
npx create-react-app rick-morty-app
This command creates a React.js app in our folder directory.
Once all of the dependencies have been installed, navigate to the rick-morty-app
directory by running
cd rick-morty-app
To start the development server, run
npm start or yarn start
To view the app, go to localhost:3000
We must install the apollo-client
library and set up some basic configurations to write queries to the GraphQL API in our React app.
Open up the terminal and run the following command:
npm install @apollo/client
When we run the specified command, it installs the apollo-client
library, which enables our React.js app to establish a connection with the Rick and Morty API. This connection lets us consume the data in the Rick and Morty API.
In your App.js
file, write the following code. I'll then go over each line of the code to explain its purpose.
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";
Import PeopleData from './PeopleData'
function App() {
const client = new ApolloClient({
cache: new InMemoryCache(),
uri: "https://rickandmortyapi.com/graphql",
});
return (
<ApolloProvider client={client}>
<div className="App">
<PeopleData />
</div>
</ApolloProvider>
);
}
export default App;
The code sets up our React app with the Apollo Client, which lets us communicate with the Rick and Morty API server. It imports the necessary classes from the @apollo/client
library, including the ApolloClient
, InMemoryCache
, and ApolloProvider
.
The Apollo Client library has good state management, allowing us to cache data in our browser called the InMemoryCache. The InMemoryCache is configured with the ApolloClient, which stores the results of queries made to the GraphQL server.
Finally, the App component returns a div containing the PeopleData component, making GraphQL queries to the server using the ApolloClient. This code enables the React app to communicate with a GraphQL API via the Apollo Client.
Let us create a file called PeopleData.js
. This file involves making a query operation to the Rick and Morty API via the Apollo client library and then displaying the retrieved data.
Go ahead and write the following code:
import React from "react";
import { useQuery, gql } from "@apollo/client";
const GET_CHARACTERS = gql`
query GetCharacters {
characters {
results {
id
name
image
}
}
}
`;
function PeopleData() {
const { loading, data } = useQuery(GET_CHARACTERS);
if (loading) {
return <h1> Loading...</h1>;
}
return (
<div>
{data.characters.results.map((character) => (
<div key={character.id}>
<h2>{character.name}</h2>
<img src={character.image} alt={character.name} />
</div>
))}
</div>
);
}
export default PeopleData;
In the PeopleData.js
file, we imported the useQuery
hook and gql
function from the @apollo/client
library. The useQuery
Hook is an essential part of the apollo client library because this hook lets us make queries to the Rick and Morty API. The gql
function lets us write GraphQL syntax in our React components.
The GET_CHARACTERS
constant is a GraphQL query retrieving the id
, name
, and image
fields for all characters in the API. The useQuery
hook lets us retrieve data, handle errors, and set loading features in our components by destructuring the data
, error
, and loading
variables from the hook.
We need to use the JavaScript function to display the data retrieved from the API. For each character in the array, our component displays the character's name and image.
If we go over to localhost:3000 in the browser, our fetched data is displayed in the order specified in our component.
We can also handle errors in our code, as the useQuery hook provides an error variable.
Conclusion
GraphQL is a flexible and powerful query language that has been increasingly popular in recent years thanks to its flexibility, efficiency, and ease of use. In this guide, we have covered the basics of GraphQL, including its key features, syntax, and concepts, as well as how to write your first GraphQL query. By following the steps outlined in this guide, you now have a solid understanding of how to use GraphQL to retrieve and manipulate data from your API and display it on the frontend, as well as the tools and resources you need to get started. Whether you are a seasoned developer or just starting, GraphQL is a valuable tool to help build better, more responsive, and more scalable apps.