83 Using Apollo Client in the Frontend
83 Using Apollo Client in the Frontend: A Comprehensive Guide to GraphQL Integration
GraphQL is becoming increasingly popular as a modern data-fetching solution for frontend applications. Unlike REST APIs, which require many endpoints, GraphQL lets us fetch data in a more flexible and efficient way. In the frontend world, Apollo Client is one of the de-facto libraries for connecting a React application (or other modern frameworks) to a GraphQL API. In this article, I will take a practical look at how to use Apollo Client in the frontend: from setup all the way to implementing queries and mutations, complete with code examples, simulations, and a flow diagram.
Why Choose Apollo Client?
As an engineer who has wrestled with fetch, AJAX, and REST APIs, I felt a huge difference when I started using Apollo Client:
- Flexible Data Fetching: We can specify exactly what data the frontend needs, without over-fetching.
- Automatic Caching: Data state is automatically synchronized and kept efficient through advanced caching.
- Centralized Error Handling: Apollo provides clear, structured error handling.
- Real-time Support: Subscriptions are built in!
- Integration with React Hooks: A more idiomatic and modern development experience.
Diagram of the Apollo Client Workflow
To understand the workflow, here is the data-fetching flow when using Apollo Client in a frontend application:
flowchart TD
A[User Action on UI] --> B[React Component Trigger Query/Mutation]
B --> C[useQuery/useMutation Hook]
C --> D[Apollo Client]
D --> E[GraphQL Server]
E --> F[Response Data]
F --> G[Apollo Cache Update]
G --> H[Component Rendered with Data]
Getting Started: Installation and Configuration
In this example, I will use React as the frontend foundation.
1. Installing the Library
1npm install @apollo/client graphql2. Configuring ApolloClient
In a React application, setup is usually done in the App.js file or in the entry file.
1// src/apolloClient.js
2import { ApolloClient, InMemoryCache } from "@apollo/client";
3
4const client = new ApolloClient({
5 uri: "https://api.example.com/graphql", // Replace with your GraphQL endpoint
6 cache: new InMemoryCache(),
7});
8
9export default client;Then, wire up the ApolloProvider at the root of the application:
1// src/index.js
2import React from "react";
3import ReactDOM from "react-dom";
4import { ApolloProvider } from "@apollo/client";
5import client from "./apolloClient";
6import App from "./App";
7
8ReactDOM.render(
9 <ApolloProvider client={client}>
10 <App />
11 </ApolloProvider>,
12 document.getElementById("root")
13);Querying Data with Apollo Client
Suppose we have the following GraphQL query:
1query GetUsers {
2 users {
3 id
4 name
5 email
6 }
7}Let’s implement it in a React component:
1import { gql, useQuery } from "@apollo/client";
2
3const GET_USERS = gql`
4 query GetUsers {
5 users {
6 id
7 name
8 email
9 }
10 }
11`;
12
13function UserList() {
14 const { loading, error, data } = useQuery(GET_USERS);
15
16 if (loading) return <p>Loading...</p>;
17 if (error) return <p>Error: {error.message}</p>;
18
19 return (
20 <ul>
21 {data.users.map(user => (
22 <li key={user.id}>
23 {user.name} ({user.email})
24 </li>
25 ))}
26 </ul>
27 );
28}Sample Output
| id | name | |
|---|---|---|
| 1 | Agung | agung@domain.com |
| 2 | Wulan | wulan@domain.com |
The component will auto-update whenever the cache changes, for example after a mutation.
Performing a Mutation: Adding Data
Suppose we have this mutation schema:
1mutation AddUser($name: String!, $email: String!) {
2 addUser(name: $name, email: $email) {
3 id
4 name
5 email
6 }
7}Integrating it with React:
1import { gql, useMutation } from "@apollo/client";
2import { useState } from "react";
3
4const ADD_USER = gql`
5 mutation AddUser($name: String!, $email: String!) {
6 addUser(name: $name, email: $email) {
7 id
8 name
9 email
10 }
11 }
12`;
13
14function AddUserForm() {
15 const [name, setName] = useState("");
16 const [email, setEmail] = useState("");
17 const [addUser, { loading, error }] = useMutation(ADD_USER, {
18 // Update the cache after the mutation succeeds
19 update(cache, { data: { addUser } }) {
20 cache.modify({
21 fields: {
22 users(existingUsers = []) {
23 const newUserRef = cache.writeFragment({
24 data: addUser,
25 fragment: gql`
26 fragment NewUser on User {
27 id
28 name
29 email
30 }
31 `,
32 });
33 return [...existingUsers, newUserRef];
34 },
35 },
36 });
37 },
38 });
39
40 const handleSubmit = e => {
41 e.preventDefault();
42 addUser({ variables: { name, email } });
43 };
44
45 return (
46 <form onSubmit={handleSubmit}>
47 <input value={name} onChange={e => setName(e.target.value)} placeholder="Name" />
48 <input value={email} onChange={e => setEmail(e.target.value)} placeholder="Email" />
49 <button type="submit" disabled={loading}>Add User</button>
50 {error && <p>Error: {error.message}</p>}
51 </form>
52 );
53}Scenario: Error Handling & Loading
Apollo helps developers handle loading and error states in an idiomatic way.
1const { loading, error, data } = useQuery(GET_USERS);
2
3if (loading) return <Spinner />;
4if (error) return <Alert>{error.message}</Alert>;Comparison Table: Apollo Client vs Fetch API
| Feature | Apollo Client | Fetch API |
|---|---|---|
| Automatic Cache | Yes | No |
| Query Builder | Yes (GQL) | Manual |
| Realtime | Yes (Subscription) | Not native |
| Pagination | Easy (Relay/Cache) | Manual |
| Error Handling | Structured (Hooks) | Manual |
Brief Case Study: Realtime Subscription
Suppose we want to listen to data in real time:
1import { gql, useSubscription } from "@apollo/client";
2
3const USER_ADDED = gql`
4 subscription OnUserAdded {
5 userAdded {
6 id
7 name
8 email
9 }
10 }
11`;
12
13function UserAddedListener() {
14 const { data, loading } = useSubscription(USER_ADDED);
15
16 if (loading) return null;
17 return <p>New user: {data.userAdded.name}</p>;
18}Conclusion
Integrating Apollo Client into the frontend makes both productivity and data management structure far better. With its ergonomic API, automatic caching, integrated error handling, and real-time support, Apollo Client meets the needs of complex and demanding modern applications. If you are working with GraphQL, Apollo Client is the best investment for your frontend codebase.
References: