Skip to content

GraphQL - React Integration

GraphQL - Modern React Integration with Apollo Client

Section titled “GraphQL - Modern React Integration with Apollo Client”

React is a popular JavaScript library for building user interfaces. This chapter explains how to integrate GraphQL into a modern React application using Apollo Client, the de facto standard library for GraphQL client-side operations in the React ecosystem.

While you can interact with a GraphQL API using standard fetch calls, libraries like Apollo Client provide significant advantages:

  • Declarative data fetching: Define data requirements alongside your UI components.
  • Intelligent Caching: Automatic caching normalized data, reducing redundant network requests.
  • UI Integration: Seamlessly integrates with React’s state and lifecycle using Hooks.
  • Developer Tools: Excellent browser extensions for inspecting queries, mutations, and cache.
  • Error Handling: Robust mechanisms for handling GraphQL and network errors.
  • State Management: Can manage both remote and local application state.
  • A running GraphQL server (we’ll set up a basic one).
  • Node.js and npm/yarn installed.
  • Basic understanding of React (functional components and Hooks).

Setting up the GraphQL Server (Modernized)

Section titled “Setting up the GraphQL Server (Modernized)”

First, let’s set up a minimal GraphQL server using Apollo Server 4 and Express. This server will provide the data for our React client.

Create a new folder (e.g., react-graphql-server) and initialize a Node.js project:

mkdir react-graphql-server
cd react-graphql-server
npm init -y
npm install @apollo/server graphql express cors body-parser

We are installing Apollo Server, GraphQL.js, Express, CORS middleware, and body-parser.

Create a schema.graphql file:

type Query {
greeting: String
sayHello(name: String!): String
}

This schema defines two simple queries: greeting and sayHello.

Create a resolvers.js file:

const resolvers = {
Query: {
greeting: () => 'Hello GraphQL World from Apollo Server!',
// The 'args' object contains the arguments passed to the query field
sayHello: (_, { name }) => `Hi ${name}! GraphQL server says Hello to you!`,
},
};
export default resolvers;
// Or if using CommonJS: module.exports = resolvers;

These functions resolve the queries defined in the schema. Note the modern use of object destructuring (_, { name }) to access arguments.

Create a server.js file:

import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import express from 'express';
import http from 'http';
import cors from 'cors';
import bodyParser from 'body-parser';
import { readFileSync } from 'fs';
// Using dynamic import for resolvers if using ES modules and resolvers.js uses module.exports
// import resolvers from './resolvers.js';
// If resolvers.js uses export default, regular import works.
const resolvers = require('./resolvers'); // Use require if using CommonJS
// Load schema
const typeDefs = readFileSync('./schema.graphql', { encoding: 'utf-8' });
async function startApolloServer() {
const app = express();
const httpServer = http.createServer(app);
// Set up Apollo Server
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
// introspection: true, // Optional: Enables schema introspection, default is true unless NODE_ENV is 'production'
});
await server.start();
app.use(
'/graphql', // The path where GraphQL endpoint will be available
cors(), // Enable CORS for all origins (adjust for production)
bodyParser.json(),
expressMiddleware(server, {
context: async ({ req }) => ({ token: req.headers.token }), // Example context setup
}),
);
const PORT = 9000;
await new Promise((resolve) => httpServer.listen({ port: PORT }, resolve));
console.log(`🚀 Server ready at http://localhost:${PORT}/graphql`);
}
startApolloServer();

This sets up Apollo Server 4 with Express, integrating the schema and resolvers. It uses expressMiddleware for handling GraphQL requests.

Add a start script to your package.json:

"scripts": {
"start": "node server.js"
// Or use nodemon for development: "start": "nodemon server.js"
}

Then run:

npm start

The server should be running at http://localhost:9000/graphql. You can open this URL in your browser to use the Apollo Sandbox (a modern alternative to GraphiQL) to test your queries:

query TestQuery {
greeting
sayHello(name: "React Developer")
}

Now, let’s create a React application and connect it to our GraphQL server using Apollo Client.

In a separate terminal window, create a React app (using Vite, a modern alternative to Create React App, is recommended, but CRA works too):

# Using Vite (Recommended)
npm create vite@latest hello-world-client --template react
cd hello-world-client
npm install
# OR Using Create React App
# npx create-react-app hello-world-client
# cd hello-world-client

Step 2: Install Apollo Client Dependencies

Section titled “Step 2: Install Apollo Client Dependencies”

Install the necessary Apollo Client packages:

npm install @apollo/client graphql

In your React app’s main entry file (e.g., src/main.jsx for Vite or src/index.js for CRA), set up the Apollo Client instance and wrap your application with ApolloProvider.

// src/main.jsx or src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css'; // Or your main CSS file
import { ApolloClient, InMemoryCache, ApolloProvider, gql } from '@apollo/client';
// Configure Apollo Client
const client = new ApolloClient({
uri: 'http://localhost:9000/graphql', // Your GraphQL server endpoint
cache: new InMemoryCache(),
});
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>
);

ApolloProvider makes the configured client available to all components in the component tree via React’s context API.

Step 4: Fetch Data in a Component using useQuery Hook

Section titled “Step 4: Fetch Data in a Component using useQuery Hook”

Modify your src/App.jsx (or src/App.js) to use the useQuery hook from Apollo Client to fetch data.

// src/App.jsx
import React, { useState } from 'react';
import { useQuery, useLazyQuery, gql } from '@apollo/client';
import './App.css'; // Example CSS import
// Define GraphQL Queries using gql tag
const GET_GREETING = gql`
query GetGreeting {
greeting
}
`;
const SAY_HELLO = gql`
query SayHello($name: String!) {
sayHello(name: $name)
}
`;
function App() {
const [nameInput, setNameInput] = useState('React User');
// useQuery: Executes the query immediately when the component mounts
const { loading: greetingLoading, error: greetingError, data: greetingData } = useQuery(GET_GREETING);
// useLazyQuery: Returns a function to execute the query manually (e.g., on button click)
const [loadHello, { loading: helloLoading, error: helloError, data: helloData }] = useLazyQuery(SAY_HELLO);
const handleSayHelloClick = () => {
if (nameInput) {
loadHello({ variables: { name: nameInput } });
}
};
return (
<div className="App">
<header className="App-header">
<h1>Modern GraphQL + React</h1>
</header>
<section>
<h2>Greeting Query (useQuery)</h2>
{greetingLoading && <p>Loading greeting...</p>}
{greetingError && <p>Error loading greeting: {greetingError.message}</p>}
{greetingData && <p>Server says: {greetingData.greeting}</p>}
</section>
<hr />
<section>
<h2>Say Hello Query (useLazyQuery)</h2>
<label htmlFor="nameInput">Enter Name: </label>
<input
id="nameInput"
type="text"
value={nameInput}
onChange={(e) => setNameInput(e.target.value)}
/>
<button onClick={handleSayHelloClick} disabled={helloLoading || !nameInput}>
{helloLoading ? 'Loading...' : 'Say Hello'}
</button>
{helloError && <p>Error saying hello: {helloError.message}</p>}
{helloData && <p>Server says: {helloData.sayHello}</p>}
</section>
</div>
);
}
export default App;

Key points in the component:

  • Import useQuery, useLazyQuery, and gql from @apollo/client.
  • Define queries using the gql template literal tag.
  • useQuery(GET_GREETING) fetches the greeting automatically.
  • useLazyQuery(SAY_HELLO) provides a loadHello function to trigger the query on demand, passing variables.
  • Both hooks return objects containing loading, error, and data states, making it easy to handle different UI states.
  • We use standard React useState for the input field.

Make sure your GraphQL server is still running (from the first terminal). In the second terminal (inside the hello-world-client directory), start the React development server:

npm run dev # If using Vite
# OR
npm start # If using Create React App

Your browser should open to the React application (usually http://localhost:5173 for Vite or http://localhost:3000 for CRA). You should see the greeting loaded automatically, and you can interact with the ‘Say Hello’ section.