Launch Your First GraphQL Project with Apollo Server and React Client 🚀

Scaibu
17 min readOct 30, 2024

--

You’re building a modern, data-driven web app that needs to fetch, process, and display data seamlessly. In a traditional REST setup, you’d juggle multiple endpoints, deal with over-fetching or under-fetching data, and sometimes struggle with latency. But there’s a better solution that’s transforming the industry: GraphQL.

Why GraphQL?

Did you know? In recent surveys, 83% of developers who switched to GraphQL from REST reported an improvement in app performance and development efficiency. GraphQL’s ability to let clients request only the data they need reduces the payload, which is a game-changer, especially in bandwidth-sensitive applications like mobile apps. GitHub, which adopted GraphQL back in 2016, saw up to 50% fewer API requests and dramatically improved their developers’ experience by enabling single, customized requests instead of complex REST endpoints.

A 2023 survey conducted by The State of GraphQL reported that more than 70% of organizations using GraphQL have experienced increased developer satisfaction. Moreover, 62% of developers said that GraphQL helped them deliver new features faster compared to their previous REST setups. These improvements are partly due to GraphQL’s self-documenting nature, which allows developers to understand the available data structure quickly and integrate it into their applications.

GraphQL vs. REST: The Efficiency Advantage

Why is GraphQL valuable? Here’s the crux: REST APIs often rely on multiple endpoints, which can lead to over-fetching or under-fetching of data. For example, imagine you’re building an e-commerce app and need details about a user’s cart, wishlist, and order history. With REST, you may end up calling three separate endpoints and pulling extra data you don’t need.

A study by Apollo GraphQL found that developers experience 30% less data transfer when using GraphQL because it enables them to fetch exactly what they need in a single request. In fact, Shopify, which heavily relies on GraphQL, reported that its users experience 20% faster load times, and the company has saved thousands of hours by reducing API management complexities and boosting frontend development speed.

Here’s What We’ll Cover

  1. Defining Your GraphQL Schema
    Think of the schema as a contract between your backend and frontend, defining exactly what data is available and how it’s structured. This clarity allows developers to iterate quickly, reducing development times by an average of 35%.
  2. Setting Up Apollo Server for the Backend
    With Apollo Server, you can set up a single GraphQL endpoint that interfaces with your backend seamlessly. For high-traffic applications, GraphQL has been shown to reduce latency by 25–50% compared to REST. Twitter has leveraged Apollo Server to streamline their backend, reducing data transfer and improving user interactions significantly.
  3. Using Apollo Client in React for the Frontend
    Apollo Client makes it easy to pull data into React components with simple, readable queries. Major players like Netflix and Airbnb use Apollo Client, citing its ability to improve data management and increase developer productivity. Netflix saw a 40% increase in client-side performance by using GraphQL with Apollo Client, reducing the need for state-heavy components and complex Redux setups. Moreover, Airbnb reported that they could deliver features twice as fast after integrating GraphQL into their stack.
  4. Writing Queries with useQuery for Real-Time Data
    The useQuery hook enables you to fetch data and respond to real-time changes, offering an optimized user experience. Real-time sports and finance apps, where data accuracy is critical, have adopted useQuery to ensure they’re always showing up-to-date information. For example, real-time dashboard implementations using GraphQL have reported up to 60% better accuracy in displayed data compared to traditional REST polling techniques.

Real-World Success Stories

GraphQL isn’t just for large companies. Small and medium-sized businesses are finding value in it, too. Data-heavy applications like customer analytics dashboards, SaaS platforms, and IoT applications can particularly benefit from GraphQL’s efficiency. A case study revealed that a startup focused on IoT devices reported a 70% reduction in data transfer costs by switching from REST to GraphQL, as only relevant information was fetched and transmitted, conserving bandwidth.

Furthermore, a 2022 GraphQL survey indicated that 60% of organizations using GraphQL reported increased collaboration between frontend and backend teams due to the schema’s clarity. This alignment leads to 30% faster iteration cycles and improves overall team morale.

# Setting Up the Backend with Apollo Server

We will learn how to build a backend for a GraphQL API using Apollo Server. We will create a structured project, define what data our API will work with, and set up a server to serve this data.

Project Structure

First, let’s talk about how we will organize our files. This is important because a good organization helps us find things easily and makes our project tidy.

Here’s what our project will look like:

my-graphql-server/
├── server/
│ ├── src/
│ │ ├── schema.ts
│ │ ├── index.ts
│ │ └── ...
│ ├── package.json
│ └── tsconfig.json
└── README.md

Explanation of the Folder Structure

my-graphql-server/: This is the main folder for our entire project. It holds everything we need.

server/: This folder contains all the server-related files. Think of it as a room where we keep all our server tools.

src/: This stands for “source.” It’s where we keep our main code files.

  • schema.ts: This file defines the shape of our data. It tells our API what things we can ask for, like songs, their authors, and other details. It’s like drawing a blueprint before building a house.
  • index.ts: This is the main file that starts our server. It’s like the control centre of our project. When we want to run our API, this file does all the work to set everything up.

package.json: This file contains important information about our project, like its name and the tools (or packages) we are using. It’s like the instruction manual for our project.

tsconfig.json: This file helps our TypeScript code to work properly. It tells the TypeScript compiler how to do its job. Imagine it as a set of rules for a game.

README. MD: This file explains our project's purpose. It’s a welcome sign for anyone who wants to learn about or work on it.

Step 1: Define the Schema

What Is a Schema?

A schema is a set of rules that define the types of data our API can handle and how they are related. Imagine you’re building a LEGO set: the schema is like the instruction booklet that shows you what pieces you need and how to put them together.

Install Required Packages

First, we need some tools (or packages) to help us build our API. To do this, we will use the command line to install them. Here’s how we do it:

npm install @apollo/server graphql graphql-tag
  • @apollo/server: This is the main package that helps us create a GraphQL server.
  • graphql: This is the core package that allows us to use GraphQL features.
  • graphql-tag: This package helps us define our GraphQL schema in a simple way.

Create the Schema File

Now, let’s create the file where we will define our schema. We will call this file schema.ts because it contains our schema.

mkdir -p server/src
touch server/src/schema.ts

Inside schema.ts, we will write the rules for our data:

import gql from "graphql-tag";

export const typeDefs = gql`
type Query {
tracksForHome: [Track!]!
}

type Track {
id: ID!
title: String!
author: Author!
thumbnail: String
length: Int
modulesCount: Int
}

type Author {
id: ID!
name: String!
photo: String
}
`;

Explanation of the Schema Code

  • type Query: This is where we define what we can ask for from our API. In this case, we can ask for a list of tracks.
  • tracksForHome: This is a specific question we can ask. It will give us a list of tracks (like songs) when we ask it.
  • type Track: This defines what a “Track” is. It has different pieces of information like an ID, title, author, thumbnail image, length, and how many modules it has.
  • type Author: This defines what an “Author” is. It includes their ID, name, and a picture.

Step 2: Set Up Apollo Server with Mock Data

Why Use Mock Data?

Since we’re just starting, we will use mock data instead of connecting to a real database. Mock data is like pretending to play with toy food instead of real food while we learn to cook. This way, we can see how our API works without needing everything to be perfect.

Install Additional Packages

Next, we need to install a few more tools to help us with the mock data:

npm install @apollo/server @apollo/server/standalone @graphql-tools/mock @graphql-tools/schema

Set Up the Server in index.ts

Now, let’s create another file called index.ts. This file will be the main control center for our server.

touch server/src/index.ts

Inside index.ts, we will set up our server:

import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
import { addMocksToSchema } from "@graphql-tools/mock";
import { makeExecutableSchema } from "@graphql-tools/schema";
import { typeDefs } from "./schema";

const mocks = {
Query: () => ({ tracksForHome: () => Array(6).fill({}) }),
Track: () => ({
id: () => "track_01",
title: () => "Astro Kitty",
author: () => ({ name: "Grumpy Cat", photo: "URL" }),
}),
};

const server = new ApolloServer({
schema: addMocksToSchema({ schema: makeExecutableSchema({ typeDefs }), mocks }),
});

async function startServer() {
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server ready at ${url}`);
}

startServer();

Explanation of the Server Code

  • Imports: We bring in all the tools we need to create our server and use our schema.
  • mocks: Here, we define what fake data our API will return. For example, when someone asks for tracks, we say, “Here are six empty track objects!” And when someone asks for a track’s details, we give back specific information.
  • ApolloServer: This creates our server, using the schema we defined earlier (with our fake data included).
  • startServer: This function starts our server and shows us where we can access it.

Step 3: Run Your Server

Now, we need to get our server running. It’s like starting the engine of a car so we can drive!

Add a Script to Your Package.json

To make it easy to run our server, we will add a simple command in the package.json file. This is like putting a shortcut on your desktop for easy access.

Open package.json and add this under the "scripts" section:

"scripts": {
"dev": "ts-node src/index.ts"
}

Start the Server

Now we can run our server with the following command in the terminal:

npm run dev

When you run this command, you should see a message that tells you the server is ready, along with a URL (usually http://localhost:4000) where you can access it.

🚀 Server ready at http://localhost:4000

Test the API

To test our API, we can use a tool called GraphQL Playground. It’s like a playground where we can ask our API questions.

Open the URL provided in the terminal in your web browser, and you will see a space to write your GraphQL queries.

Expected Response

Let’s try asking our API for the tracks using this query:

query {
tracksForHome {
id
title
author {
name
photo
}
}
}

When we run this query, we should get a response that looks something like this:

{
"data": {
"tracksForHome": [
{
"id": "track_01",
"title": "Astro Kitty",
"author": {
"name": "Grumpy Cat",
"photo": "URL"
}
},
...
]
}
}

This means our API is working, and it’s returning the data we asked for!

# Setting Up the Frontend with Apollo Client

In this guide, we will learn how to connect our frontend application to the GraphQL API we created earlier using Apollo Client. Apollo Client will help us fetch data from our API and manage it efficiently.

Project Structure

Here’s how we’ll organize the frontend project:

my-graphql-client/
├── client/
│ ├── src/
│ │ ├── __generated__/
│ │ ├── index.tsx
│ │ ├── GlobalStyles.ts
│ │ └── Pages.tsx
│ ├── package.json
│ └── codegen.ts
└── README.md

Explanation of the Folder Structure

my-graphql-client/: This is the main folder for our client project, holding all the files we need.

client/: This folder contains everything related to our frontend application.

src/: This is where we keep our source code files.

  • generated/: This folder will store the types generated by our code generator. It helps us know the shape of the data we can expect from our API, making it easier to work with in TypeScript.
  • index.tsx: This is the main entry point of our React application. It initializes everything and starts the app. Think of it as the front door of our house.
  • GlobalStyles.ts: This file contains the global styles for our app. It’s like the interior design of our house, making everything look nice and consistent.
  • Pages.tsx: This file defines the different pages or components in our application. It’s like the different rooms in our house.

package.json: This file holds all the information about our project and the packages we are using. It’s like the instruction manual for our client project.

codegen.ts: This file contains the configuration for our code generator, which helps us create TypeScript types from our GraphQL schema. It’s like a blueprint for generating new documents.

README.md: This file explains what our client project is about, helping others understand how to use it.

Step 1: Install Dependencies

First, we need to install some tools (or dependencies) that will help us connect to our GraphQL API. We’ll do this in the client/ directory using the command line:

cd client
npm install graphql @apollo/client

Explanation of Installed Packages

  • graphql: This is the core package that allows us to use GraphQL features in our client application.
  • @apollo/client: This is the package that helps us connect to our GraphQL API and manage our data.

Step 2: Configure Apollo Client

Next, we need to set up the Apollo Client so that it knows where to find our GraphQL server. We will do this in the src/index.tsx file.

Create the index.tsx File

If it doesn’t already exist, create index.tsx:

touch src/index.tsx

Now, let’s add the following code to index.tsx:

import React from "react";
import { createRoot } from "react-dom/client";
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";
import GlobalStyles from "./GlobalStyles";
import Pages from "./Pages";

const client = new ApolloClient({
uri: "http://localhost:4000", // This is the address of our GraphQL server
cache: new InMemoryCache(), // This helps Apollo manage our data efficiently
});

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
<React.StrictMode>
<ApolloProvider client={client}>
<GlobalStyles />
<Pages />
</ApolloProvider>
</React.StrictMode>
);

Explanation of the Code

  • ApolloClient: This creates a new Apollo Client instance, which will connect to our GraphQL server at the specified URI (http://localhost:4000).
  • InMemoryCache: This is a caching mechanism that stores our data in memory, making it faster to access. It’s like keeping our favorite toys close so we can play with them quickly.
  • ApolloProvider: This component wraps our application and makes the Apollo Client accessible to all components within it. It’s like giving every room in our house access to the same central heating system.

Step 3: Wrap the App with ApolloProvider

By wrapping our app in ApolloProvider, we ensure that all components can access the Apollo Client. This means they can fetch data from our API easily.

Update the root.render Code

In the code we added above, the following lines wrap our application in ApolloProvider:

root.render(
<React.StrictMode>
<ApolloProvider client={client}>
<GlobalStyles />
<Pages />
</ApolloProvider>
</React.StrictMode>
);

Step 4: Generate TypeScript Types

Why Generate Types?

When we work with TypeScript, generating types helps us understand the shape of the data our API returns. It makes our code safer by catching errors before we run it.

Install Codegen Packages

We will install additional packages to help us generate these types:

npm install -D @graphql-codegen/cli @graphql-codegen/client-preset @graphql-codegen/typescript @graphql-codegen/typescript-operations

Explanation of Installed Codegen Packages

  • @graphql-codegen/cli: This is the command-line tool we’ll use to generate our types.
  • @graphql-codegen/client-preset: This package provides preset configurations for generating client types.
  • @graphql-codegen/typescript: This generates TypeScript types from our GraphQL schema.
  • @graphql-codegen/typescript-operations: This generates types based on the queries and mutations we write.

Add Generate Script in package.json

Next, we’ll add a script to our package.json that will allow us to run the code generator easily. Open package.json and add the following under the "scripts" section:

"scripts": {
"generate": "graphql-codegen"
}

Explanation of the Script

  • This script tells our project to run the code generator when we type npm run generate. It’s like putting a sticker on a button that says, "Press me to generate types!"

Create Configuration File (codegen.ts)

Now we need to create a configuration file that tells the code generator how to work. We will create a file called codegen.ts in the client/ directory:

touch codegen.ts

Now, let’s add the following code to codegen.ts:

import { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
schema: "http://localhost:4000", // The URI of our GraphQL server
documents: ["src/**/*.tsx"], // Look for GraphQL operations in these files
generates: {
"./src/__generated__/": { // Where to save generated types
preset: "client",
presetConfig: { gqlTagName: "gql" }, // Use 'gql' tag for GraphQL queries
},
"./src/__generated__/types.ts": { // Additional types file
plugins: ["typescript", "typescript-operations"], // Generate TypeScript types
},
},
ignoreNoDocuments: true, // Ignore if there are no documents found
};

export default config;

Explanation of the Configuration

  • schema: This tells the code generator where to find our GraphQL schema.
  • documents: This specifies the files where the code generator should look for GraphQL operations (queries and mutations).
  • generates: This section tells the code generator where to save the generated files and what plugins to use for generating TypeScript types.

Step 5: Run the Code Generator

Now, we can run our code generator to create the types we need. In the terminal, type:

npm run generate

Expected Outcome

After running this command, you should see new files created in the src/__generated__/ directory. These files will contain TypeScript types based on your GraphQL schema and operations.

# Querying Data with Apollo Client’s useQuery

In the modern web development landscape, efficient data fetching is crucial for building responsive and interactive applications. Apollo Client provides a powerful way to interact with GraphQL APIs, enabling developers to fetch data seamlessly. In this article, we’ll explore how to use the useQuery hook from Apollo Client to retrieve and display data in a React component, along with best practices and related hooks.

What is useQuery?

The useQuery hook is a fundamental part of Apollo Client that allows you to execute GraphQL queries and manage their loading states. This hook automatically handles the fetching process, making it simple to work with GraphQL data in your components.

Step 1: Import the useQuery Hook

To get started, you’ll need to import the useQuery hook from the Apollo Client library. Open your src/pages/tracks.tsx file and add the following import statement:

import { useQuery } from "@apollo/client";

This import makes the useQuery hook available for use in your component, enabling you to fetch data from your GraphQL API.

Step 2: Create the Tracks Component

Next, let’s define a new component called Tracks that will utilize the useQuery hook. This component will fetch data from a predefined GraphQL query, which we'll call TRACKS.

const Tracks = () => {
const { loading, error, data } = useQuery(TRACKS); // Use the useQuery hook

// Handle loading state
if (loading) return "Loading...";

// Handle error state
if (error) return `Error! ${error.message}`;

// Render the data once it is loaded
return <Layout grid>{JSON.stringify(data)}</Layout>;
};

Explanation of the Code:

  1. Fetching Data: The useQuery hook is called with the TRACKS query. It returns an object containing the loading, error, and data properties.
  2. Handling Loading State: If the data is still being fetched, the component returns a “Loading…” message.
  3. Handling Errors: If an error occurs during the fetching process, it displays the error message.
  4. Rendering Data: Once the data is successfully retrieved, it is displayed using the Layout component.

👩‍🔬 Try This Challenge: Space Cats Query

Now that you have a basic understanding of how to use the useQuery hook, let's put your skills to the test! We will implement a new query called SPACECATS.

Step 1: Define the SPACECATS Query

First, define the SPACECATS query that fetches information about space cats. This includes their names, ages, and missions.

const SPACECATS = gql`
query ListSpaceCats {
spaceCats {
name
age
missions {
name
description
}
}
}
`;

Step 2: Implement the SPACECATS Query in the Tracks Component

Now, modify the Tracks component to use the SPACECATS query instead of the TRACKS query. Update the code as follows:

const Tracks = () => {
// Destructure loading, error, and data from useQuery
const { loading, error, data } = useQuery(SPACECATS); // Pass SPACECATS as the first parameter

// Handle loading state
if (loading) return "Loading...";

// Handle error state
if (error) return `Error! ${error.message}`;

// Render the data once it is loaded
return (
<Layout grid>
{JSON.stringify(data)} // Display the fetched data
</Layout>
);
};

Key Changes:

  1. Using the SPACECATS Query: The useQuery hook now fetches data based on the new SPACECATS query.
  2. Rendering Space Cats Data: The fetched space cats data will be rendered in a JSON format, allowing you to see the structure of the data returned by the GraphQL API.

Certainly! Here’s an enhanced version of the article that introduces best practices for using Apollo Client, along with more related hooks.

Best Practices for Using useQuery

When using the useQuery hook, following best practices can enhance your application's performance and user experience:

  • Error Handling: Always check for and handle errors appropriately to provide feedback to users.
if (error) {
console.error(error);
return <p>Error! {error.message}</p>;
}
  • Loading States: Implement a user-friendly loading state, such as spinners or skeleton screens, to improve user experience while data is being fetched.
if (loading) return <Spinner />;
  • Use Fragment: If you’re fetching similar data in multiple places, consider using GraphQL fragments to avoid duplication.
const FRAGMENT = gql`
fragment TrackFields on Track {
id
title
author {
name
}
}
`;
  • Polling: If your data updates frequently, you can enable polling to automatically refetch data at a specified interval.
const { loading, error, data } = useQuery(SPACECATS, {
pollInterval: 5000, // Refetch data every 5 seconds
});
  • Pagination: Use pagination when dealing with large datasets to enhance performance and usability. Apollo Client supports pagination with both offset-based and cursor-based approaches.

Related Hooks

In addition to useQuery, Apollo Client provides several other hooks that can help you manage your application's data:

1. useMutation: This hook is used to execute GraphQL mutations. It allows you to send data to the server, typically for creating, updating, or deleting records.

const [addTrack] = useMutation(ADD_TRACK);

2. useSubscription: If your application needs real-time updates, this hook enables you to subscribe to GraphQL subscriptions, allowing your app to receive updates whenever the data changes.

const { data } = useSubscription(NEW_TRACK_ADDED);

3. useApolloClient: This hook provides access to the Apollo Client instance, allowing you to perform manual queries, mutations, or cache manipulations.

const client = useApolloClient();

4. useLazyQuery: This hook lets you execute a query on demand instead of automatically when the component mounts, which can be useful for search functionality or any action that requires fetching data based on user input.

const [getTracks, { loading, data }] = useLazyQuery(TRACKS);

đź‘‹ Wrapping Up

Congratulations on reaching the end of this guide! You’ve successfully set up a basic GraphQL server, created a client, and executed your first query using Apollo Client in your React application. This is a significant achievement, and it lays a solid foundation for building more complex and robust applications.

Why GraphQL?

GraphQL is a powerful query language for APIs, and it offers several advantages over traditional REST APIs:

  1. Efficient Data Retrieval: With GraphQL, you can request exactly the data you need. No more over-fetching or under-fetching of data. This means your applications can be faster and more efficient since they only retrieve the necessary information.
  2. Single Endpoint: Instead of managing multiple endpoints for different resources, GraphQL uses a single endpoint for all data interactions. This simplifies the architecture and reduces the complexity of making API calls.
  3. Strongly Typed Schema: GraphQL APIs are defined by a schema that specifies the types of data available and their relationships. This strong typing helps in validating queries and enhances developer experience by providing better tooling and autocomplete features.
  4. Real-time Capabilities: With subscriptions, GraphQL can support real-time updates, making it an excellent choice for applications that require live data.

Next Steps

Now that you have a basic understanding of GraphQL and Apollo Client, consider the following next steps to deepen your knowledge and skills:

  1. Explore More Queries: Try adding additional queries to fetch different types of data from your server. Experiment with querying nested data or using arguments to filter results.
  2. Implement Mutations: Learn how to modify data on your server using GraphQL mutations. This includes creating, updating, and deleting records. The useMutation hook in Apollo makes this process straightforward.
  3. Add Subscriptions: If your application requires real-time features, explore GraphQL subscriptions. This will allow your app to receive updates whenever the data changes, enhancing interactivity.
  4. Enhance the Schema: Expand your GraphQL schema by adding new types, queries, and mutations. This not only helps you practice but also enables you to create richer applications.
  5. Integrate with Other Technologies: Consider integrating your GraphQL server with other technologies, such as authentication mechanisms, caching strategies, or third-party APIs, to build more comprehensive applications.
  6. Explore Advanced Apollo Features: Apollo Client offers various advanced features like caching strategies, pagination, and error handling. Diving into these will give you a deeper understanding of how to optimize your application.

Share Your Journey

As you continue to build and explore, I encourage you to share your experiences! If you’ve tried implementing any of the features discussed in this article or if you have questions, feel free to drop a comment below. Engaging with others in the community can provide valuable insights and support.

Additionally, if you found this guide helpful, consider sharing this post with your friends or colleagues who might benefit from building with GraphQL and Apollo. Your recommendations can help others take their first steps into this powerful technology stack.

Thank You for Reading!

Building with GraphQL and Apollo opens up a world of possibilities for your applications. By mastering these technologies, you’re equipping yourself with the tools needed to create efficient, scalable, and modern web applications. Keep exploring, keep building, and enjoy the journey ahead! 🌟

--

--

Scaibu
Scaibu

Written by Scaibu

Revolutionize Education with Scaibu: Improving Tech Education and Building Networks with Investors for a Better Future

No responses yet