Telerik blogs

In this article, we cover the differences between RESTful APIs and GraphQL and how we can leverage the power of GraphQL and Apollo Client to build efficient, scalable and modern applications.

As the field of frontend development evolves, so does the complexity of the data it manages. Traditional methods of accessing APIs, such as RESTful APIs, are no longer sufficient due to their inherent limitations. One major issue with RESTful APIs is over-fetching. This happens when we request data and the server sends back too much data than we need, resulting in wasted resources and increased response times.

Another drawback is under-fetching, which occurs when a request returns less data than is required. This often leads to making multiple requests to different endpoints, which can negatively impact an application’s performance. These problems of traditional APIs can be easily solved with GraphQL.

GraphQL and Apollo Client are powerful tools for enhancing performance and effectively managing application data. In this article, we’ll discuss how to use GraphQL and Apollo Client for efficient data fetching and state management in API-driven applications.

Understanding GraphQL

GraphQL is a query language, as indicated by the “QL” in its name. Its syntax allows us to query or mutate data on a server. GraphQL can improve an application’s performance and scalability by helping us retrieve the exact data we require from the server.

The key feature of GraphQL is that it gives the client exactly the data it requested, unlike traditional RESTful APIs. Hence, it provides a more efficient, powerful and flexible way of data fetching in API-driven applications.

Differences Between GraphQL and RESTful APIs

Let’s take a simple example to understand the difference between GraphQL and RESTful APIs. Imagine you are building a simple blog application with the following data model:

  1. User
  • ID
  • Username
  • Email
  1. Post
  • ID
  • Title
  • Content
  • Author(user)

RESTful API

For a RESTful API, you may have endpoints like:

  • /users (GET): Get a list of all users.
  • /users/{id} (GET): Get details of a specific user.
  • /posts (GET): Get a list of all posts.
  • /posts/{id} (GET): Get details of a specific post.

To get a list of posts with the author’s username, you will need to make multiple requests. For example:

Get a list of posts:

# Request 1: Get list of posts
GET /posts

# Response 1:
[
  {
    "id": 1,
    "title": "Introduction to GraphQL",
    "content": "GraphQL is awesome!",
    "authorId": 123
  },
  // More posts...
]

For each post, get the details of the author using the user ID.

# Request 2: Get details of the author for post with ID
GET /users/123

# Response 2:
{
  "id": 123,
  "username": "john_doe",
  "email": "john@example.com"
}

GraphQL

With GraphQL, you can request the specific data you need through a single query, as shown in the example below:

# GraphQL Query
query {
  posts {
    id
    title
    content
    author {
      id
      username
    }
  }
}

// Response
{
  "data": {
    "posts": [
      {
        "id": 1,
        "title": "Introduction to GraphQL",
        "content": "GraphQL is awesome!",
        "author": {
          "id": 123,
          "username": "john_doe"
        }
      },
      // More posts...
    ]
  }
}

GraphQL eliminates the problems of over-fetching and under-fetching data by helping you get exactly what you requested. If you look closely at our REST API example above, you will notice multiple endpoints. Each endpoint returns its data structure, potentially including information the app does not need.

To get a list of posts with the author’s username, you need to make at least two separate requests. This can lead to network redundancy in more complex applications. However, GraphQL offers a more efficient and flexible approach to data fetching with just a single endpoint and precise data requests to improve performance and network efficiency.

Core Concepts of GraphQL

Schema Definition Language (SDL)

The schema is essentially the blueprint of GraphQL API. It can be seen as a contract between the client and the server. The schema specifies how the data can be requested and what types of responses should be expected.

A schema describes the structure and relationship of your data. It can be seen as a map of everything your API exposes. With schema, clients see exactly what data is available and can request specific subsets with optimized queries. It provides a concise and human-readable way to express the types, queries and mutations that make up the API, thereby shielding the client from the backend complexities. You can read more about schemas here.

Queries

In GraphQL, queries are used to request specific data from a GraphQL server. Unlike traditional REST APIs, which often receive a fixed set of data, GraphQL allows clients to define the structure of the response and request only the information they need. Queries in GraphQL are written using syntax that resembles the shape of the data you want to retrieve.

Let’s look at an example of a query to fetch a list of users and their names:

query GetUsers {
  users {
    id
    name
  }
}
The response to the query:
{
  "data": {
    "users": [
      {
        "id": "123",
        "name": "John Doe"
      },
      {
        "id": "456",
        "name": "Jane Doe"
      }
    ]
  }
}

Modify Data with Mutations

Apart from fetching data from our GraphQL API, we can also mutate data. That is, we can create, update and delete data from our API. Mutations are operations used to modify data on the server. Here is a simple example of mutations to create and update data:

mutation CreateUser($name: String!, $email: String!) {
  createUser(name: $name, email: $email) {
    id
    name
  }
}

The code snippet above creates a new user.

mutation UpdatePost($id: ID!, $title: String!) {
  updatePost(id: $id, title: $title) {
    id
    title
  }
}

This mutation updates the title of a specific blog post.

Subscriptions

Subscriptions enable clients and servers to communicate in real-time. While queries are used for retrieving data and mutations for modifying data, subscriptions allow clients to receive real-time updates when certain events occur on the server. This is useful for building applications that require live data, such as chat applications, live notifications or any scenario where you want to push updates to clients as soon as they are available.

Introduction to Apollo Client

Apollo Client is a powerful JavaScript library used to manage state and interact with GraphQL APIs in client-side applications. It is a key part of the Apollo ecosystem, which also includes server-side components like Apollo Server. Apollo Client simplifies the process of working with GraphQL by providing tools for managing application state, making queries to a GraphQL server and handling real-time updates through subscriptions.

Integrating Apollo Client in a React Application

We are going to build a simple application that uses Apollo Client and GraphQL API in a React application. We will be using the Rick and Morty API.

Project Setup

To create a React project, run the the following command on your terminal:

npx create-react-app apollo-graphql-demo
cd apollo-graphql-demo
npm start

Installing and Configuring Apollo Client

To configure Apollo Client into our project, we need to install the necessary dependencies. Run the following command in your terminal:

npm install @apollo/client graphql

Initialize an Instance of ApolloClient

To initialize ApolloClient, in your src folder, create a Graphql folder. Inside the folder, create a file named apolloClient.js, and add the following code to it:

import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client";
const client = new ApolloClient({
  link: new HttpLink({ uri: "https://rickandmortyapi.com/graphql" }),
  cache: new InMemoryCache(),
});
export default client;

The code above initializes a new instance of ApolloClient with a link to the Rick and Morty GraphQL API and sets up an in-memory cache for query results.

Define GraphQL Queries

Create a file in your Grapghql folder named queries.js, and add the following to it:

import gql from "graphql-tag";
export const GET_CHARACTERS = gql`
  query GetCharacters {
    characters {
      results {
        id
        name
        status
        species
        image
      }
    }
  }
`;

The code below defines a GraphQL query using the gql function. The GET_CHARACTERS constant is assigned the result of calling the gql \***\* function with a template literal. This template contains a GraphQL query named GetCharacters. The query is structured to fetch information about characters and includes the fields id, name, status, species and image \*\*** for each character.

Wrap Your Root App with Apollo Client Provider

Now that we have successfully defined our queries, the next step is to wrap our React app with an ApolloProvider.

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { ApolloProvider } from "@apollo/client";
import client from "./aplloClient";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
    ,
  </React.StrictMode>
);

Fetch Your Data

We are going to use the useQuery hook to request our data. The useQuery hook is used to execute GraphQl queries in a React component. It simplifies the process of sending GraphQL queries to a server and handling the response.

To display the data in our app, create a folder named components. Inside the folder, create a file named Character.js, and add the following to it:

import React from "react";
import { useQuery } from "@apollo/client";
import { GET_CHARACTERS } from "../graphql/queries";
function Characters() {
  const { loading, error, data } = useQuery(GET_CHARACTERS);
  if (loading) return <p>Loading characters...</p>;
  if (error) return <p>Error fetching characters</p>;
  return (
    <div>
      <h2>Character List</h2>
      <ul className="characters-grid">
        {data.characters.results.map((character) => (
          <li key={character.id}>
            <img src={character.image} alt={character.name} />
            <div>
              <strong>{character.name}</strong>
              <p>Status: {character.status}</p>
              <p>Species: {character.species}</p>
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
}
export default Characters;

Next let’s call the Character.js component in the App.js file:

import "./App.css";
import Characters from "./components/Characters";
function App() {
  return (
    <div className="App">
      <h1>Rick and Morty Characters</h1>
      <Characters />
    </div>
  );
}
export default App;

If you have reached this point, you should see the list of characters in the Rick and Morty API, as shown below.

Rick and Morty Characters

Conclusion

In this article, we covered the differences between Restful APIs and GraphQL and how we can leverage the power of GraphQL and Apollo Client to build efficient, scalable and modern applications.


About the Author

Christian Nwamba

Chris Nwamba is a Senior Developer Advocate at AWS focusing on AWS Amplify. He is also a teacher with years of experience building products and communities.

Related Posts

Comments

Comments are disabled in preview mode.