Published on 5.3.2020
In the world of modern web development, APIs are the backbone of data communication between the client and server. Over the years, the two primary ways to fetch data have been REST APIs and GraphQL APIs. While REST has been the traditional choice for years, GraphQL has been rapidly gaining traction due to its flexibility, efficiency, and developer-friendly features.
In this post, we will:
- Explore when to choose GraphQL over REST.
- Walk through implementing Apollo Client in a React project.
- Demonstrate how to query data efficiently with GraphQL hooks.
Let’s dive in!
When to Choose GraphQL Over REST
Before we dive into implementation, let’s first understand the core differences between REST and GraphQL and when each is suitable.
1. Data Fetching Needs
- REST API: With REST, you typically make a request to a specific endpoint (e.g.,
/users
,/posts
) for a specific resource. However, with REST, you often end up requesting more data than you need (over-fetching) or have to make multiple requests to different endpoints to gather related data (under-fetching). - GraphQL: In contrast, GraphQL allows you to request only the exact data you need with a single query. You define the shape of the response in your query, which helps avoid over-fetching and under-fetching of data.
2. Flexibility
- REST API: A REST API typically returns a fixed structure of data for an endpoint. This means that changes to the data structure usually require modifications on the server side.
- GraphQL: GraphQL provides more flexibility because clients can ask for exactly what they need, and the server only needs to send the data the client asks for.
3. Versioning
- REST API: As your application grows, the need to update or change endpoints arises, which often leads to versioning (e.g.,
/v1/posts
,/v2/posts
). This can make maintaining and evolving a REST API complex. - GraphQL: GraphQL eliminates the need for versioning. Since the client defines what data it wants, and the server can evolve the schema without breaking the client, versioning is no longer necessary.
4. Real-Time Data
- REST API: REST APIs are typically request/response-based and don’t offer built-in support for real-time data.
- GraphQL: GraphQL supports subscriptions, which allow real-time communication between the client and the server. This is useful for apps that require live updates, such as chat applications, stock tickers, etc.
5. Learning Curve
- REST API: REST is relatively easy to understand and implement. The concepts of HTTP methods (GET, POST, PUT, DELETE) and resource endpoints are straightforward.
- GraphQL: GraphQL introduces some new concepts, such as schemas, queries, mutations, and subscriptions. While it may take a bit longer to learn, the benefits of more efficient data fetching and flexibility often outweigh the learning curve.
Implementing Apollo Client in a React Project
Now that we understand the key differences between GraphQL and REST, let’s take a look at how to integrate Apollo Client, a popular GraphQL client, into a React project.
1. Install Apollo Client
First, install Apollo Client and the required dependencies in your React project:
npm install @apollo/client graphql
2. Set Up Apollo Client
Next, configure Apollo Client in your React application. Create an ApolloClient
instance and wrap your application with the ApolloProvider
to provide the client throughout your app.
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider, InMemoryCache } from '@apollo/client';
import App from './App';
// Initialize Apollo Client
const client = new ApolloClient({
uri: 'https://your-graphql-endpoint.com/graphql', // Replace with your GraphQL endpoint
cache: new InMemoryCache(),
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);
Here:
uri
: Set this to your GraphQL endpoint.cache
: Apollo Client uses an in-memory cache to store data, which improves performance by reducing the need for repeated requests.
3. Fetching Data with Apollo Client
To query data using Apollo Client, we use the useQuery
hook provided by Apollo. Here’s an example of how to fetch data in a React component:
import React from 'react';
import { useQuery, gql } from '@apollo/client';
// Define a GraphQL query
const GET_USERS = gql`
query {
users {
id
name
email
}
}
`;
function UsersList() {
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Users List</h1>
<ul>
{data.users.map(user => (
<li key={user.id}>
<strong>{user.name}</strong> - {user.email}
</li>
))}
</ul>
</div>
);
}
export default UsersList;
Here:
useQuery(GET_USERS)
: This hook fetches the data based on the defined query (GET_USERS
).- Apollo Client handles the loading, error, and data states for us.
Querying Data Efficiently with GraphQL Hooks
One of the great advantages of using GraphQL with React is the useQuery hook, which helps to fetch data efficiently and keeps your components in sync with the server. Let’s look at a few strategies for efficient querying with GraphQL hooks.
1. Using Variables in Queries
GraphQL allows you to pass variables to queries, which helps in dynamically fetching data. Here’s an example where we query users based on a search term:
import React, { useState } from 'react';
import { useQuery, gql } from '@apollo/client';
const SEARCH_USERS = gql`
query searchUsers($name: String!) {
users(name: $name) {
id
name
email
}
}
`;
function SearchUsers() {
const [name, setName] = useState('');
const { loading, error, data } = useQuery(SEARCH_USERS, {
variables: { name },
skip: !name, // Skip the query if name is empty
});
return (
<div>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder="Search for a user"
/>
{loading && <p>Loading...</p>}
{error && <p>Error: {error.message}</p>}
{data && (
<ul>
{data.users.map(user => (
<li key={user.id}>
<strong>{user.name}</strong> - {user.email}
</li>
))}
</ul>
)}
</div>
);
}
export default SearchUsers;
In this example:
- Variables: We pass the
name
variable to the query to fetch users by name. skip
: We use theskip
option to prevent the query from running when there’s no search term.
2. Pagination with GraphQL
When dealing with large datasets, pagination is essential. GraphQL allows you to paginate data with cursor-based pagination. Here’s a basic example:
const GET_USERS_PAGINATED = gql`
query getUsersPaginated($cursor: String) {
users(cursor: $cursor) {
id
name
email
cursor
}
}
`;
function PaginatedUsers() {
const { data, loading, fetchMore } = useQuery(GET_USERS_PAGINATED);
const loadMore = () => {
fetchMore({
variables: { cursor: data.users[data.users.length - 1].cursor },
});
};
if (loading) return <p>Loading...</p>;
return (
<div>
<h1>Users</h1>
<ul>
{data.users.map(user => (
<li key={user.id}>
<strong>{user.name}</strong> - {user.email}
</li>
))}
</ul>
<button onClick={loadMore}>Load More</button>
</div>
);
}
Here:
fetchMore
: This method allows us to load more data (e.g., for pagination) without reloading the entire list.
Conclusion
In 2020, GraphQL continues to be a game-changer, especially for React developers. It offers a more efficient and flexible way to handle data fetching compared to traditional REST APIs. By allowing clients to request exactly the data they need, GraphQL helps optimize performance and reduces the complexity of managing multiple endpoints.
Whether you’re building a new app or considering a migration, Apollo Client and GraphQL hooks offer a powerful solution for efficient, scalable data management. If you haven’t already, it’s time to consider switching to GraphQL for your React apps!