Hello World! In this blog, I am going to discuss how we can use React hooks with Apollo to connect to GraphQL API in different scenarios. Assuming that you have a basic understanding of the same, I will be explaining how GraphQL data can be shared with the UI using React hooks by giving a few examples that we are already using in our ongoing project, the Litmus Portal.
What is GraphQL?
Before going forward, let me give a very brief overview of GraphQL and what all things we are going to discuss. So GraphQL is a query language for APIs that is developed by Facebook. It is an efficient alternative to REST because of its features such as:
With GraphQL there's no over fetching or under fetching of data, unlike REST.
Strongly typed graphQL schema which can be written in GraphQL Schema Definition Language (SDL) helps you validate your API requests during its compile time.
With the development of various GraphQL libraries (Apollo, Relay, etc) you are getting a lot of features such as caching, realtime data, etc.
It provides a large and amazing community! You can always get your queries answered whenever stuck.
This was just a basic introduction to GraphQL, but I recommend you to visit the site to gain deeper insights of the same.
What we'll do?
I will be mainly focussing on the front-end side, where I am gonna explain the two very useful react hooks useQuery and useMutation, how are we using these in our project for GraphQL operations along with the code.
Wait...what are GraphQL operations?
GraphQL provides various types of operations such as Query, Mutation, and Subscription which act as the entry points for the requests sent by the client. In this blog, I'll be discussing the first two types i.e. Query and Mutation.
Query:useQuery hook is used to fetch the data from the server and attach it to the UI. To run a query you need to call this hook by passing the query string, it returns an object from Apollo client containing data, error, loading properties which change their values after execution. The hook is called when the component renders and the above properties can be used conditionally to render the UI.
data : The required data we are getting after the query is successful.
loading : It is a boolean value, if true, it means the query is still in flight. After it is successful the value of loading changes to false.
error : It stores the error if occurred while querying.
Mutation:useMutation hook is used to send updates to the GraphQL server as a result of which data can be updated in the back-end. It is somewhat similar to useQuery in terms of syntax with some minor differences. To execute a mutation, you need to pass the mutation string to the hook. This hook returns a tuple containing a mutate function which can be called whenever its execution is required and an object having certain fields that represent the current status of mutation's execution.
mutateFunction : It is the mutate function which can be called anytime to run the mutation.
The second parameter is the object representing the mutation's execution status such as error , loading which have been explained above.
In both examples, I have added options to the hooks:
onCompleted : It is a callback executed after a successful query/mutation.
onError : Callback executed in case of an occurrence of error.
refetchQueries : It takes an array or function which is used to specify a list of queries that need to be refetched after the mutation is successful.
Some of the good practices you can follow:
Type the data you are sending or receiving during the requests wherever it is required. It enhances readability and understandability.
As a beginner we often tend to store the data we received from the requests in local states which is not required. Apollo Client provides an in-memory cache in which it stores the data that helps the client to respond to future queries for the same data without sending unnecessary requests. So instead of storing it in local states we can directly access and use it without making repeated requests.
Now I'll be explaining some examples that we have used in our ongoing project, the Litmus Portal.
LitmusChaos
LitmusChaos is an open-source toolset to practice chaos engineering in cloud-native systems. It comes up with a large set of chaos experiments that are hosted on the hub. For further details, you can check out our github repo. Litmus Portal provides a console and UI experience for managing, monitoring, and events around chaos workflows. It is being developed using React and TypeScript for the front-end and Golang for the back-end.
Examples
Without any further delay, let's get started!!
Since the examples I am going to explain are a part of a project, I have excluded some parts of the logic which are not relevant to the blog. You can find the complete code here.
Query
Schema
export const GET_USER = gql`
query getUser($username: String!) {
getUser(username: $username) {
username
email
id
name
projects {
members {
user_id
user_name
role
invitation
name
email
joined_at
}
name
id
}
company_name
updated_at
created_at
removed_at
is_email_verified
state
role
}
}
`;
export const ALL_USERS = gql`
query allUsers {
users {
id
name
username
email
}
}
`;
The GET_USER query string returns the complete details of a user whose username is passed as a variable.
The ALL_USERS query string returns a list of all users who are present along with their details including id, name, username, and email.
useQuery
const { data: dataB } = useQuery<CurrentUserDetails, CurrentUserDedtailsVars>(
GET_USER,
{ variables: { username: userData.username } }
);
const { data: dataA } = useQuery(ALL_USERS, {
skip: !dataB,
onCompleted: () => {
//consoles the list of all users present
console.log(dataA.users);
},
onError: (error) => {
//in case of error, it prints the error message in the console
console.error(error.message)
});
In the above example, I have two queries:
GET_USER : I am sending the username as variable to get all the details associated with that username. The received data can be accessed through dataB. CurrentUserDedtailsVars is the type of data I am sending i.e. the username and CurrentUserDetails is the type of data I am receiving on a successful query. These types are stored in a separate file:
ALL_USERS : This query is for fetching the list of all the users which can be accessed through dataA.
skip : This is a boolean value, if true, the query will be skipped. In the above logic if dataB is empty i.e. unless and until GET_USER query is successful ALL_USERS query will be skipped. Once dataA gets populated then the second query is executed. This option is useful in the cases where you need to execute the queries in specific order.
The SEND_INVITE mutation string is used to send an invitation to a user for a selected project. Once the user accepts the invitation he/she becomes a member of that project too. As MemberInput we need to send the data which includes the id of the project, the username of the user whom we are going to send the invitation, the role of user in the project name Viewer or Editor.
So these were some of the examples of GraphQL mutation and query. I hope I was able to explain these concepts well, but if you still have some queries or feedback feel free to reach out to me. Since the LitmusChaos project is completely open-source, feel free to contribute in any way possible. Visit the GitHub repo and become one out of the many stargazers.
Litmus helps SREs and developers practice chaos engineering in a Cloud-native way. Chaos experiments are published at the ChaosHub (https://hub.litmuschaos.io). Community notes is at https://hackmd.io/a4Zu_sH4TZGeih-xCimi3Q
LitmusChaos is an open source Chaos Engineering platform that enables teams to identify weaknesses & potential outages in infrastructures by
inducing chaos tests in a controlled way. Developers & SREs can practice Chaos Engineering with LitmusChaos as it is easy to use, based on modern
Chaos Engineering principles & community collaborated. It is 100% open source & a CNCF project.
LitmusChaos takes a cloud-native approach to create, manage and monitor chaos. The platform itself runs as a set of microservices and uses Kubernetes
custom resources (CRs) to define the chaos intent, as well as the steady state hypothesis.
At a high-level, Litmus comprises of:
Chaos Control Plane: A centralized chaos management tool called chaos-center, which helps construct, schedule and visualize Litmus chaos workflows
Last but not the least, with the upcoming Hacktober Fest, there are many issues for all levels such as good-first issues, front-end issues, complex issues, etc. So even if you are a beginner you can always submit a PR and start contributing to open source. Grab your chance to win a lot of Litmus swags and goodies upon a successful merge. So do not forget to visit the Litmus site and join our community(#litmus channel on the Kubernetes Slack).😇