DEV Community

Cover image for Comprehensive guide to GraphQL clients, part 3
Drago
Drago

Posted on

Comprehensive guide to GraphQL clients, part 3

React-Relay

Relay is a GraphQL client which is built by Facebook. It is a great GraphQL client, but it is not a good choice for simple applications and beginners. It is highly opinionated, users must follow the strict rules.
The main thing here is that the error possibility is minimal. Contrary to other clients it misses a lot of flexibility. Relay demands a lot of knowledge about the application and schema design.
The shiny point is that it is awesome for scalability. Relay is a complete framework for declarative data fetching.
To be able to use Relay, one must create a GraphQL server compatible with Relay.
Many developers avoid using Relay and instead use Apollo Client for complex applications, due to its simplicity and flexibility. It is hard to set up compared to other frameworks, but when everything is done, it is less error-prone and can be used in production serving a lot of users.
The big advantage of React Relay is that it is supported by Facebook and is tested by millions of users on their social networks. In the new release of React18, the Suspense feature is deeply integrated with Relay. So, if you are using React for your application, with Relay you can get a great performance boost.

From the official documentation:

Relay is composed of three core parts:

  • Relay Compiler: A GraphQL to GraphQL optimizing compiler, providing general utilities for transforming and optimizing queries as well as generating build artifacts. A novel feature of the compiler is that it facilitates experimentation with new GraphQL features - in the form of custom directives - by making it easy to translate code using these directives into standard, spec-compliant GraphQL.
  • Relay Runtime: A full-featured, high-performance GraphQL runtime that can be used to build higher-level client APIs. The runtime features a normalized object cache, optimized "write" and "read" operations, a generic abstraction for incrementally fetching field data (such as for pagination), garbage collection for removing unreferenced cache entries, optimistic mutations with arbitrary logic, support for building subscriptions and live queries, and more.
  • React/Relay: A high-level product API that integrates the Relay Runtime with React. This is the primary public interface to Relay for most product developers, featuring APIs to fetch the data for a query or define data dependencies for reusable components (e.g. useFragment).

Installation:
For some reason, I have got an error when using the npm package manager. So I'm using yarn instead.

yarn add relay-runtime react-relay
yarn add --dev relay-compiler babel-plugin-relay
Enter fullscreen mode Exit fullscreen mode

Add schema.graphql to your project:

cd my-project
curl https://raw.githubusercontent.com/ogurenko/graphql-api/main/schema.graphql > schema.graphql
Enter fullscreen mode Exit fullscreen mode

Modify package.json to run relay compiler:

"scripts": {
 "start": "yarn run relay && react-scripts start",
 "build": "yarn run relay && react-scripts build",
 "relay": "yarn run relay-compiler"
 },
 "relay": {
 "src": "./src/",
 "schema": "./schema.graphql"
 },
 ...
Enter fullscreen mode Exit fullscreen mode

Now you can run the app:

yarn start
Enter fullscreen mode Exit fullscreen mode

You should see your app running.
Now it is time to configure the Relay environment. Create a file called RelayEnvironment.js in src/ and add the following code:

import { Environment, Network, RecordSource, Store } from "relay-runtime";
import FetchedData from "./FetchData";


export default new Environment({
 network: Network.create(() => FetchedData()),
 store: new Store(new RecordSource()),
});
Enter fullscreen mode Exit fullscreen mode

Add FetchData.js to src/ and add the following code:

async function FetchedData() {

 const response = await fetch("https://countries.trevorblades.com/", {
 method: "POST",
 headers: {
 "Content-Type": "application/json",
 },
 body: JSON.stringify({
 query: "{countries { name }}",
 }),
 });

 return await response.json();
}

export default FetchedData;
Enter fullscreen mode Exit fullscreen mode

Replace the contents of src/App.js with the following code:

import React from "react";
import graphql from "babel-plugin-relay/macro";
import {
 RelayEnvironmentProvider,
 loadQuery,
 usePreloadedQuery,
} from "react-relay/hooks";
import RelayEnvironment from "./RelayEnvironment";

const { Suspense } = React;


const countriesQuery = graphql`
 query AppCountryNamesQuery {
 countries {
 name
 }
 }
`;


const preloadedQuery = loadQuery(RelayEnvironment, countriesQuery, {
 /* query variables */
});

function App(props) {
 const data = usePreloadedQuery(countriesQuery, props.preloadedQuery);
console.log(data.countries);
 const countriesName = [];
 data.countries.map((c) => countriesName.push(c.name));
 return (
 <div className="App">
 <header className="App-header">
 {countriesName?.map((c, index) => <ul key={index}>{c}</ul>)}
 </header>
 </div>
 );
}

function AppRoot() {
 return (
 <RelayEnvironmentProvider environment={RelayEnvironment}>
 <Suspense fallback={"Loading..."}>
 <App preloadedQuery={preloadedQuery} />
 </Suspense>
 </RelayEnvironmentProvider>
 );
}

export default AppRoot;
Enter fullscreen mode Exit fullscreen mode

Modify src/index.js to run the app with the latest version of React:

import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

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

root.render(
 <React.StrictMode>
 <App />
 </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

You're done!

Note: To find out more about the environment and particular hooks in the Relay system, please refer to the official documentation

Apollo Client

At the end of this comprehensive guide, you will learn how to use Apollo Client to fetch data from a GraphQL server.
Apollo is a gold standard between the GraphQL ecosystem and React. It's simple, powerful, and flexible. It has a lot of features for modern applications. The downside is the bigger size of the library. It is not convenient to use this "mega" library for a simple application.
Apollo is a platform-agnostic client. It can be used with any framework on the client-side, as well as with vanilla JavaScript. Works well with TypeScript and serverless architectures. Works well with Node.js and supports many libraries.

Installation:

npm install @apollo/client graphql
Enter fullscreen mode Exit fullscreen mode

Create a client:

  • App.js:
import React from 'react'
import { ApolloProvider, ApolloClient, InMemoryCache } from "@apollo/client";
import { FetchedData } from "./FetchData";

const client = new ApolloClient({
 uri: "https://countries.trevorblades.com",
 cache: new InMemoryCache()
});

export default function App() {
 return (
 <ApolloProvider client={client}>
 <FetchedData />
 </ApolloProvider>
 );
}
Enter fullscreen mode Exit fullscreen mode

FetchData.js:

import React from "react";
import { useQuery, gql } from "@apollo/client";

const countriesQuery = gql`
 query {
 countries {
 name
 }
 }
`;

export const FetchedData = () => {
 const { loading, error, data } = useQuery(countriesQuery);

 if (loading) return <p>Loading ... </p>;
 if (error) return <p>Erorr ... </p>;

 const countriesList = data.countries.map((country, index) => (
 <ul key={index}>{country.name}</ul>
 ));
 return (
 <>
 <h1>Countries</h1>
 {countriesList}
 </>
 );
};
Enter fullscreen mode Exit fullscreen mode

Start the app:

npm start
Enter fullscreen mode Exit fullscreen mode

As you can see, Apollo Client leverages the power of React Context and Hooks. Additionally resolves the problems with caching, state management, and error handling.

Bundle size and popularity of the GraphQL clients:

graphql-request graphql-hooks urql React-Relay apollo-client
Size
Stars
Forks
Issues
Downloads
  • red flag: the worst result among all the clients
  • green flag: the best result among all the clients
  • Size: source Bundlephobia (mini zipped size of the bundle)
  • Stars & Issues & Forks: source Github
  • Downloads: source NPM

The table above shows that GraphQL-request and urql are overall the most balanced libraries. There are no extreme results in any of these two clients.
Apollo-client is the most popular client in the community. On the other hand, it has the most issues.
GraphQL-hooks is the least popular client in the community. That's not suggesting that is the bad solution for your application.
GraphQL-request is the most downloaded client, so it speaks for itself.

Conclusion

Axios, React-Query, and SWR are great tools for any protocol and can be used to build a GraphQL client. My focus is on the protocol-specific GraphQL clients.
The GraphQL clients presented in this post, are all pretty solid and have a lot of good features. You can find a lot of comparison tables and examples of how to use the GraphQL clients on the internet. But this post aims to show you practical examples, with the same query and different GraphQL clients. I think that the best way to learn is to try it out on the same workload and see how it works.

Opinionated recommendations:

  • For the sake of simplicity, I recommend using graphql-request. It is a simple, fast, and small library that is easy to use.

  • If you need data on many pages, across different components, you can use graphql-hooks, which uses React Context and Hooks.

  • If you are looking for a GraphQL client that is more powerful, I recommend using urql. Although it is less popular than Apollo Client, it has a lot of good features. Great post about it: I Switched From Apollo To Urql (And It Was Worth It)

  • If you need an enterprise solution, you can use react-relay. The learning curve is a bit more complex, but once you set up the environment, your app is less likely to break.

Thanks for reading!

References:

5 GraphQL clients for JavaScript and Node.js

Axios vs. fetch(): Which is best for making HTTP requests?

Urql

React-Query

Top comments (1)

Collapse
 
nurbol_n_caf464bc57f278da profile image
Nurbol N

Hi, thanks for the article.
How do you deal with generated readonly types if you pass them to editable form?
TS will not allow any editing due to readonly fields, and I dont want to use type assertions, looks like a hacky solution