Introduction
If you've worked with traditional REST APIs, you've probably run into some frustrating limitations. Maybe you needed just a user's name and email, but the API sent back their entire profile data. Or perhaps you needed data from multiple endpoints, forcing you to make several requests just to populate a single page.
These are exactly the kinds of problems GraphQL was designed to solve. Created by Facebook engineers who faced these same challenges at scale, GraphQL has transformed how modern web applications handle data fetching.
In this blog, I'll walk you through the basics of GraphQL by building a simple React app that fetches user info using Apollo Client.
What is GraphQL?
At its heart, GraphQL is a query language for your API. Think of it as a more precise way to ask your server for exactly what you need. Unlike REST, where the server dictates what data you get from each endpoint.
Imagine walking into a restaurant where instead of ordering from a fixed menu, you can tell the chef exactly what ingredients you want in your meal. That's GraphQL—you specify precisely what fields you need, and the server returns just those fields. No more, no less.
Why Developers Are Switching to GraphQL
But after using it in several projects, I'm convinced it solves real problems:
One endpoint to rule them all: Instead of remembering dozens of REST endpoints, you interact with a single GraphQL endpoint for all your data needs.
No more data bloat: You ask for exactly what you need. If you only want a user's name, you only get their name—saving bandwidth and improving performance.
Self-documenting APIs: GraphQL APIs come with built-in type systems and introspection, so you can discover what queries are possible without leaving your development environment.
Developer tools that actually help: The ecosystem includes amazing tools like GraphQL Playground and Apollo DevTools that make debugging and exploring APIs surprisingly pleasant.
Setting Up Our Project
We'll create a simple app that pulls user data from a public GraphQL API.
First, let's set up a React project with vite, which gives us a lightning-fast development experience:
npm create vite@latest graphql-demo -- --template react-ts
cd graphql-demo
npm install
Next, we need to install Apollo Client and GraphQL:
npm install @apollo/client graphql
Apollo Client does much more than just send GraphQL requests—it provides a complete data management solution with intelligent caching, optimistic UI updates, and integration with React's component lifecycle.
Finding a GraphQL API to Play With
For this tutorial, we'll use GraphQL Zero, a free online GraphQL API that mimics typical data you might encounter in production. It's perfect for learning because it requires no authentication and provides realistic data structures.
Here's a sample of what we can query:
{
user(id: 1) {
id
name
email
}
}
This asks for a user with ID 1, and specifically requests their id, name, and email fields—nothing more.
Building Our App with Apollo Client
Step 1: Setting Up the Apollo Provider
First, we need to configure Apollo Client and wrap our application with the ApolloProvider
. This makes the GraphQL client available throughout our component tree:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";
// Create our Apollo Client instance
const client = new ApolloClient({
uri: "https://graphqlzero.almansi.me/api", // The GraphQL endpoint
cache: new InMemoryCache(), // Apollo's caching system
});
// Wrap our App with the Apollo Provider
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>
);
The InMemoryCache
is one of Apollo's most powerful features. It automatically stores query results and helps avoid unnecessary network requests when you request the same data again.
Step 2: Defining Our GraphQL Query
Next, let's create a dedicated file for our GraphQL queries. This helps keep our code organized as our application grows:
// src/graphql/queries.ts
import { gql } from "@apollo/client";
export const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
The gql
tag parses our GraphQL query string into the format Apollo needs. The $id
parameter lets us dynamically specify which user to fetch.
Step 3: Using the Query in Our Component
Now for the fun part—let's update our App component to fetch and display the user data:
// src/App.tsx
import { useQuery } from "@apollo/client";
import { GET_USER } from "./graphql/queries";
// TypeScript interface for our user data
interface User {
id: string;
name: string;
email: string;
}
function App() {
// The useQuery hook handles the entire request lifecycle
const { loading, error, data } = useQuery<{ user: User }>(GET_USER, {
variables: { id: 1 }, // Pass our query variables
});
// Handle loading state
if (loading) return <p>Loading...</p>;
// Handle errors
if (error) return <p>Error fetching user: {error.message}</p>;
const user = data?.user;
// Handle case where no user was found
if (!user) return <p>No user found.</p>;
// Render the user data
return (
<div style={{ padding: "2rem" }}>
<h1>👤 User Info</h1>
<p><strong>ID:</strong> {user.id}</p>
<p><strong>Name:</strong> {user.name}</p>
<p><strong>Email:</strong> {user.email}</p>
</div>
);
}
export default App;
The useQuery
hook is where the magic happens. It fetches our data when the component mounts, handles loading and error states, and re-renders the component with the result. Notice how clean the data-fetching logic is—no manual state management or effect hooks required!
Step 4: Running Our Application
With everything in place, start the development server:
npm run dev
Navigate to http://localhost:5173, and you should see the user information displayed. Behind the scenes, Apollo Client sent your GraphQL query to the server, received exactly the data you asked for, and cached it for future use.
Why This Approach Shines
What we've built might seem simple, but it demonstrates several advantages of the GraphQL + Apollo approach:
Declarative data fetching: Our component declares what data it needs right alongside the code that uses it.
Built-in loading and error states: Apollo handles the request lifecycle so we can focus on rendering.
Type safety: With TypeScript and GraphQL's type system working together, we get end-to-end type safety.
Caching by default: Apollo intelligently caches our data, improving performance for subsequent requests.
Conclusion
This introduction just scratches the surface of what's possible with React and GraphQL. As you get more comfortable, you might want to explore:
- Mutations for creating, updating, and deleting data
- More advanced querying with fragments and nested queries
- Pagination and infinite scrolling
- Local state management with Apollo Client
- Optimistic UI updates for a snappier feel
GraphQL has fundamentally changed how we think about data fetching in frontend applications. If you like this blog and want to learn more about Frontend Development and Software Engineering, you can follow me on Dev.to.
Top comments (0)