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.
Why Apollo Client?
Section titled “Why Apollo Client?”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.
Prerequisites
Section titled “Prerequisites”- 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.
Step 1: Project Setup and Dependencies
Section titled “Step 1: Project Setup and Dependencies”Create a new folder (e.g., react-graphql-server) and initialize a Node.js project:
mkdir react-graphql-servercd react-graphql-servernpm init -ynpm install @apollo/server graphql express cors body-parserWe are installing Apollo Server, GraphQL.js, Express, CORS middleware, and body-parser.
Step 2: Define Schema (schema.graphql)
Section titled “Step 2: Define Schema (schema.graphql)”Create a schema.graphql file:
type Query { greeting: String sayHello(name: String!): String}This schema defines two simple queries: greeting and sayHello.
Step 3: Create Resolvers (resolvers.js)
Section titled “Step 3: Create Resolvers (resolvers.js)”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.
Step 4: Set up Apollo Server (server.js)
Section titled “Step 4: Set up Apollo Server (server.js)”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 schemaconst 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.
Step 5: Run the Server
Section titled “Step 5: Run the Server”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 startThe 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")}Setting up the React Client with Apollo
Section titled “Setting up the React Client with Apollo”Now, let’s create a React application and connect it to our GraphQL server using Apollo Client.
Step 1: Create a React Project
Section titled “Step 1: Create a React Project”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 reactcd hello-world-clientnpm install
# OR Using Create React App# npx create-react-app hello-world-client# cd hello-world-clientStep 2: Install Apollo Client Dependencies
Section titled “Step 2: Install Apollo Client Dependencies”Install the necessary Apollo Client packages:
npm install @apollo/client graphqlStep 3: Configure Apollo Client
Section titled “Step 3: Configure Apollo Client”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.jsimport 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 Clientconst 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.jsximport React, { useState } from 'react';import { useQuery, useLazyQuery, gql } from '@apollo/client';import './App.css'; // Example CSS import
// Define GraphQL Queries using gql tagconst 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, andgqlfrom@apollo/client. - Define queries using the
gqltemplate literal tag. useQuery(GET_GREETING)fetches the greeting automatically.useLazyQuery(SAY_HELLO)provides aloadHellofunction to trigger the query on demand, passing variables.- Both hooks return objects containing
loading,error, anddatastates, making it easy to handle different UI states. - We use standard React
useStatefor the input field.
Step 5: Run the Client Application
Section titled “Step 5: Run the Client Application”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# ORnpm start # If using Create React AppYour 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.
Further Learning
Section titled “Further Learning”- Apollo Client Documentation: https://www.apollographql.com/docs/react/
- React Hooks: https://react.dev/reference/react
- Handling Mutations with
useMutationhook. - Managing Local State with Apollo Client.
- Authentication flows with Apollo Link.