Web Development

Integrating GraphQL with React: A Step-by-Step Guide

When combined with React, a JavaScript library for building user interfaces, GraphQL offers developers a seamless way to fetch and manage data. In this step-by-step guide, we'll explore how to integrate GraphQL with React to create dynamic and data-driven applications.

By Laxaar Engineering Team Feb 20, 2024 3 min read
Integrating GraphQL with React: A Step-by-Step Guide

Fixed endpoints make you take what you get. GraphQL flips that: the client asks for exactly the fields it needs, nothing more. Pair that with React's component model and you get a tight feedback loop between what a component renders and what it fetches. Here's how to wire them together.

Why Integrate GraphQL with React?

Efficient Data Fetching

REST endpoints return a fixed shape. If your UI needs three fields, you still get thirty. GraphQL lets the client name exactly what it wants in the query, so the response matches the UI's actual requirements. Less wasted bandwidth, fewer nullable fields to guard against.

Strongly Typed Schema

You define every type and relationship up front in the schema. That definition doubles as documentation and acts as a contract between client and server. If a query asks for a field that doesn't exist, GraphQL rejects it before the request even hits a resolver.

Declarative Data Fetching in React

Both React and GraphQL are declarative: you describe what you want, not how to get it. That shared philosophy makes them a natural fit. Queries and mutations live right next to the components that use them, so when a UI requirement changes, the data shape changes with it in the same file.

Getting Started

1. Set Up a GraphQL Server

You need a GraphQL server before the client has anything to talk to. Apollo Server and Express GraphQL are the two most common choices. Either one lets you define a schema and wire up resolvers that handle queries and mutations.

2. Install Required Dependencies

Apollo Client is the standard choice here. It handles caching, request lifecycle, and React integration out of the box.

npm install @apollo/client graphql
3. Configure Apollo Client

Create a client instance, point it at your server's URI, and wrap your app in ApolloProvider. Every component below that provider can now run queries and mutations.

// src/index.js
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';

const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql',
  cache: new InMemoryCache()
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById('root')
);

Querying Data

Fetching Data with GraphQL Queries

The useQuery hook takes a gql-tagged query and returns loading, error, and data. Define the query at the top of the file, pass it to the hook, and let Apollo handle the request.

// src/components/Posts.js
import { useQuery, gql } from '@apollo/client';

const GET_POSTS = gql`
  query GetPosts {
    posts {
      id
      title
      body
    }
  }
`;

const Posts = () => {
  const { loading, error, data } = useQuery(GET_POSTS);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      {data.posts.map(post => (
        <div key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
};

export default Posts;
Rendering Data in React Components

Once data is available, it's just a JavaScript object. Map over arrays, read properties, pass values as props. The same patterns you'd use with any state.

Mutating Data

Updating Data with GraphQL Mutations

Mutations handle writes: creating, updating, and deleting records. The useMutation hook works the same way as useQuery. Pass it a gql operation and get back a function you call when the user takes action.

// src/components/AddPost.js
import { useMutation, gql } from '@apollo/client';

const ADD_POST = gql`
  mutation AddPost($title: String!, $body: String!) {
    addPost(title: $title, body: $body) {
      id
      title
      body
    }
  }
`;

const AddPost = () => {
  let titleInput, bodyInput;
  const [addPost] = useMutation(ADD_POST);

  const handleSubmit = e => {
    e.preventDefault();
    addPost({
      variables: {
        title: titleInput.value,
        body: bodyInput.value
      }
    });
    titleInput.value = '';
    bodyInput.value = '';
  };

  return (
    <form onSubmit={handleSubmit}>
      <input ref={node => { titleInput = node; }} />
      <textarea ref={node => { bodyInput = node; }} />
      <button type="submit">Add Post</button>
    </form>
  );
};

export default AddPost;

Conclusion

The setup here is intentionally minimal — one server, one client instance, two hooks. That's enough to cover most CRUD use cases. As your app grows, Apollo's caching layer becomes more valuable: query results are normalized by default, so a mutation that updates a post will automatically refresh every component rendering that post's fields. Worth understanding early rather than retrofitting later.

Working on something like this?

Get a fixed scope, timeline, and price within one business day — no obligation.

React GraphQL setupGraphQL with ReactGraphQL mutations in React
Grow your business with us

Take your business to the next level.

Tell us what you're building. We'll come back inside one business day with a fixed scope, timeline, and team — or an honest “this isn't a fit”.

ENGINEERING PHILOSOPHY

Code is useless if it's not comprehensible to those who maintain it. We write code the next person can actually understand.