Getting started with React & Apollo Client
What is Apollo Client
Apollo Client is a complete state management library for JavaScript apps.
It makes use of a GraphQL API to handle data fetching. What this means is in order to make use of Apollo Client, you need to have a GraphQL API that you would connect to.
What is GraphQL
GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. GraphQL makes use of Mutation and Query to achieve this.
What is a Query and Mutation
- Query: A GraphQL query is used to read or fetch data.
A sample GraphQL query is shown in the example below.
{
query getUserDetails {
users {
id
name
email
}
}
}
Note: The above query is named
getUserDetails
and it gets back the id, name and email fields.
- Mutation: Mutations are used for any type of request that changes the data, creating, updating and deleting operations.
A sample GraphQL mutation looks like the example shown below.
{
mutation addUser(name: String!, email: String!){
addUser(name: $name, email: $email){
id
name
email
created_at
}
}
}
Note: In the mutation example above, it receives
name
and
Setup React Application
I'll be using create-react-app boilerplate to setup my react application. On your terminal run the command below to generate a react boilerplate
npx create-react-app rick-and-morty
after the above command completes, open the generated folder in your Integrated Development Environment.
Install Dependencies
npm install apollo-boost graphql react-apollo
Connect Client
To connect the react application to the Graphql API, in the index.js
file of your react application, add the following code below.
import ApolloClient from "apollo-boost";
import { ApolloProvider } from 'react-apollo';
const client = new ApolloClient({
uri: 'https://rickandmortyapi.com/graphql/', //URL of the GraphQL server
});
...then wrap your sub-components with the ApolloProvider, passing in the client we defined above as prop. An example is shown below.
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById("root")
);
Once the above is done, we have successfully setup a basic apollo client connection to the backend GraphQL API.
Note: Find more here, for more advanced Apollo client setup and configurations.
There are different methods of consuming a Graphql API when using Apollo Client, they are:
- Render Props
- Hooks
- Higher-Order Component (HOC)
All the different methods of consuming a GraphQL API can be done with the use of the react-apollo
package we installed earlier.
Queries
Render Props
To make queries with the Render Prop method, we need to use the Query
component from react-apollo
. An example is shown below.
import React from "react";
import { Query } from "react-apollo";
import { gql } from "apollo-boost";
const GET_CHARACTERS = gql`
query getCharacters {
characters {
results {
id
name
image
}
}
}
`;
export default function CharacterWithRender() {
return (
<Query query={GET_CHARACTERS}>
{({ loading, error, data }) => {
if (loading) return "Loading...";
if (error) return `Error! ${error.message}`;
return (
<div className="characters">
{data.characters.results.map(character => (
<div key={character.name} className="character">
<img src={character.image} alt={character.name} />
<p>{character.name}</p>
</div>
))}
</div>
);
}}
</Query>
);
}
Hooks
To make queries with the Hooks method, we need to use the useQuery
hook from react-apollo
. An example is shown below.
An example is shown below
import React from "react";
import { gql } from "apollo-boost";
import { useQuery } from "react-apollo";
const GET_CHARACTERS = gql`
query getCharacters {
characters {
results {
id
name
image
}
}
}
`;
function CharacterWithHook() {
const { loading, error, data } = useQuery(GET_CHARACTERS);
if (error) {
return <div>Error</div>;
}
if (loading) {
return (
<div className="App">
<h2>Loading...</h2>
</div>
);
}
if (data) {
if (data.characters.results.length > 0) {
return (
<div className="characters">
{data.characters.results.map(character => (
<div key={character.name} className="character">
<img src={character.image} alt={character.name} />
<p>{character.name}</p>
</div>
))}
</div>
);
}
}
}
export default CharacterWithHook;
Higher Order Component (HOC)
We can also use the withApollo
Higher Order Component to make queries, you can do so by simply wrapping your component's export with withApollo
. This injects a client prop into the component, thus enables you to make GraphQL queries.
An example is shown below
import React, { useState } from "react";
import { gql } from "apollo-boost";
import { withApollo } from "react-apollo";
const GET_CHARACTERS = gql`
query getCharacters {
characters {
results {
id
name
image
}
}
}
`;
function CharacterWithHOC({ client }) {
const [characters, setCharacters] = useState([]);
client
.query({ query: GET_CHARACTERS })
.then(res => setCharacters(res.data.characters.results))
.catch(err => console.log(err));
if (characters.length > 0) {
return (
<div className="characters">
{characters.map(character => (
<div key={character.name} className="character">
<img src={character.image} alt={character.name} />
<p>{character.name}</p>
</div>
))}
</div>
);
}
return (
<div className="App">
<h2>Loading...</h2>
</div>
);
}
export default withApollo(CharacterWithHOC);
Mutations
Render Props
To make mutations with the Render Prop method, we need to use the Mutation
component from react-apollo
. An example is shown below.
import React, { useState } from 'react';
import { Mutation } from 'react-apollo';
import { gql } from 'apollo-boost';
const LOGIN_MUTATION = gql`
mutation userLogin($email: String!, $password: String!) {
userLogin(email: $email, password: $password) {
username
email
id
token
}
}
`;
export default function MutationWithRender() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<Mutation mutation={LOGIN_MUTATION}>
{(loginUser, { loading, error, data }) => {
if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;
return (
<form
id="signinForm"
className="text-center p-4"
onSubmit={e => {
e.preventDefault();
loginUser({ variables: { email, password } });
}}
>
<p className="h4 mb-4 f-1">Sign In</p>
<input
title="Email"
id="email"
name="email"
value={email}
onChange={e => setEmail(e.target.value)}
type="email"
required
/>
<input
title="Password"
id="password"
name="password"
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
required
/>
<div className="form-group my-4">
<button className="btn btn-block" type="submit">
Sign In
</button>
</div>
</form>
);
}}
</Mutation>
);
}
Hooks
To make mutations with the Hooks method, we need to use the useMutation
hook from react-apollo
. An example is shown below.
An example is shown below
import React, { useState } from 'react';
import { useMutation } from 'react-apollo';
import { gql } from 'apollo-boost';
const LOGIN_MUTATION = gql`
mutation userLogin($email: String!, $password: String!) {
userLogin(email: $email, password: $password) {
username
email
id
token
}
}
`;
export function MutationWithHook() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loginUser, { data, error, loading }] = useMutation(LOGIN_MUTATION);
if (error) {
alert('Error Logging In User');
}
if (data) {
alert('Successfully Logged In');
}
return (
<form
id="signinForm"
className="text-center p-4"
onSubmit={e => {
e.preventDefault();
loginUser({ variables: { email, password } });
}}
>
<p className="h4 mb-4 f-1">Sign In</p>
<input
title="Email"
id="email"
name="email"
value={email}
onChange={e => setEmail(e.target.value)}
type="email"
required
/>
<input
title="Password"
id="password"
name="password"
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
required
/>
<div className="form-group my-4">
<button className="btn btn-block" type="submit">
Sign In
</button>
</div>
</form>
);
}
export default MutationWithHook;
Higher Order Component (HOC)
We can also use the withApollo
Higher Order Component to make mutations, you can do so by simply wrapping your component's export with withApollo
. This injects a client prop into the component, thus enables you to make GraphQL mutations.
An example is shown below
import React, { useState } from 'react';
import { withApollo } from 'react-apollo';
import { gql } from 'apollo-boost';
const LOGIN_MUTATION = gql`
mutation userLogin($email: String!, $password: String!) {
userLogin(email: $email, password: $password) {
username
email
id
token
}
}
`;
export function MutationWithHOC({ client }) {
const [error, setError] = useState(false);
const [success, setSuccess] = useState(false);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { mutate } = client;
const onSubmit = async e => {
try {
e.preventDefault();
const res = await mutate({
mutation: LOGIN_MUTATION,
variables: {
email,
password
}
});
setSuccess(res.data);
} catch (err) {
setError(err);
}
};
if(error) {
alert('Error Logging In User');
}
if (success) {
alert('Successfully Logged In');
}
return (
<form id="signinForm" className="text-center p-4" onSubmit={onSubmit}>
<p className="h4 mb-4 f-1">Sign In</p>
<input
title="Email"
id="email"
name="email"
value={email}
onChange={e => setEmail(e.target.value)}
type="email"
required
/>
<input
title="Password"
id="password"
name="password"
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
required
/>
<div className="form-group my-4">
<button className="btn btn-block" type="submit">
Sign In
</button>
</div>
</form>
);
}
export default withApollo(MutationWithHOC);
Conclusion
A lot more can be accomplished with the Apollo Client, like caching, refetching, subscriptions and a whole lot more.
In this article, we looked at how to set up a basic apollo client with react, the different methods we can use to make queries and mutations and also examples of how to make queries and mutations in our react components.
Find the code used in this project on Github
Find more information on Apollo Client here
If you have any questions or feedback about this article, feel free to leave a comment.
Thanks for reading.
This article was originally published on my blog
hello