GraphQL is a technology that is being used by many companies today (like Facebook, for example). It is becoming an important alternative for building reliable, scalable, and high-performance APIs.
Facebook released a new version a few months ago and is making heavy use of React, GraphQL, and the newest version of Relay in their application. Facebook has been using Relay in production for a few years and it is helping them to have a more scalable, stable, and maintainable application.
In this post, we’ll cover the new version of Relay. We will explore how the newest version is working and how we can create more reliable and scalable React and GraphQL applications.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
Relay is a powerful JavaScript framework for working with GraphQL in React applications. Relay is different from other GraphQL clients because it’s more structured and opinionated.
Relay helps to build more scalable, structured, and high-performance React and GraphQL applications. It makes data-fetching in GraphQL easy, relying on GraphQL best practices such as fragments, connections, global object identification, etc.
The newest version of Relay released on March 9th, 2021 and has a more developer-friendly API. The new version supports React Suspense, although it’s an experimental feature.
Relay Hooks are a new set of APIs for fetching and managing GraphQL data in React applications using React Hooks.
To start with the newest version of Relay, let’s first create a new React application using create-react-app and set up Relay:
npx create-react-app graphql-relay-example --template typescript
After creating our React application, we need to install a few packages to get started with Relay:
yarn add react-relay relay-runtime isomorphic-fetch yarn add --dev @types/react-relay @types/relay-runtime graphql relay-compiler relay-compiler-language-typescript
Now, we create a file called environment.tsx, which is where we’re going to create our Relay environment. The best way to use Relay is to use a GraphQL API that’s compatible with Relay, it will follow the best practices and make it easier to implement some things such as pagination.
Inside our environment.tsx file, put the following code:
import {
Environment,
Network,
RecordSource,
Store,
RequestParameters,
Variables
} from "relay-runtime";
import fetch from "isomorphic-fetch";
function fetchQuery(operation: RequestParameters, variables: Variables) {
return fetch('https://podhouse-server.herokuapp.com/graphql', {
method: "POST",
headers: {
Accept: "application/json",
"Content-type": "application/json",
},
body: JSON.stringify({
query: operation.text,
variables,
}),
}).then((response: any) => {
return response.json()
})
}
const network = Network.create(fetchQuery);
const env = new Environment({
network,
store: new Store(new RecordSource(), {
gcReleaseBufferSize: 10,
}),
});
export default env;
Inside our index.tsx, we import the RelayEnvironmentProvider and pass our environment to it:
import React from "react";
import ReactDOM from "react-dom";
import { RelayEnvironmentProvider } from "react-relay/hooks";
import environment from "./environment";
ReactDOM.render(
<RelayEnvironmentProvider environment={environment}>
<React.StrictMode>
<App />
</React.StrictMode>
</RelayEnvironmentProvider>,
document.getElementById('root')
);
Now, we have Relay set up on our project and we can start to use the newest version and see how it works.
useLazyLoadQueryThe easiest way of fetching data with Relay is by using the useLazyLoadQuery. This hook will fetch data during render, turning it into not the most efficient way of fetching data but the simplest:
import { useLazyLoadQuery } from "react-relay/hooks";
useLazyLoadQuery(query, variables, options);
Here’s how the useLazyLoadQuery works:
query — You need this to pass your GraphQL query template literalvariables — Is an object containing values to fetch the query. For example, when you want to authenticate a user inside your applicationoptions — Is an object that you define a few properties. fetchPolicy is used to determine if the data should be cached or not. fetchKey is used to force the reevaluation of the querynetworkCacheConfig is an object that you can define to your cache config optionsThe useLazyLoadQuery should always be used inside a RelayEnvironmentProvider. The useLazyLoadQuery may suspend your data when a network request is in flight depending on which fetchPolicy you choose. Depending on the fetchPolicy selected you should make use of React Suspense for loading states in your application.
Check out an example of the useLazyLoadQuery hook:
import React from "react";
import graphql from "babel-plugin-relay/macro";
import { useLazyLoadQuery } from "react-relay/hooks";
const query = graphql`
query SettingsQuery {
currentUser {
id
_id
email
}
}
`;
const Component = () => {
const data = useLazyLoadQuery(
query,
{},
{
fetchPolicy: "store-and-network",
}
);
return (
<div>
<h1>{data.currentUser.email}</h1>
</div>
);
};
export default Component;
usePreloadedQueryThe usePreloadedQuery is the most recommended hook for fetching data with Relay. It implements the render-as-you-fetch pattern, a pattern that allows us to load the data that we need and render our component in parallel.
The usePreloadedQuery can be a bit confusing, so let’s clear up how this hook works:
usePreloadedQuery makes use of the useQueryLoader, which is another hook available on the new version of RelayuseQueryLoader is a hook for safely load queries. It will keep a query reference stored and dispose of it when the component is disposeduseQueryLoader is designed to be used with usePreloadedQueryuseQueryLoader returns a queryReference, a loadQuery callback, and a disposeQuery callbackloadQuery callback from useQueryLoader first, which will store a query reference in React statequeryReference to our usePreloadedQuery, which will allow us to fetch data earlier while not blocking rendering on our componentThe usePreloadedQuery is the most powerful and recommended way of fetching data with Relay.
Check out an example of usage of the usePreloadedQuery hook:
import React, { useEffect } from "react";
import graphql from "babel-plugin-relay/macro";
import { useQueryLoader, usePreloadedQuery } from "react-relay/hooks";
const query = graphql`
query UserQuery($_id: ID!) {
user(_id: $_id) {
id
_id
name
}
}
`;
const Component = () => {
const [queryReference, loadQuery, disposeQuery] = useQueryLoader(query);
useEffect(() => {
loadQuery({ _id: _id }, { fetchPolicy: "store-or-network" });
return () => {
disposeQuery();
};
}, [loadQuery, disposeQuery, _id]);
return (
<React.Suspense fallback="Loading user...">
{queryReference != null ? <UserComponent queryReference={queryReference} /> : null
}
</React.Suspense>
);
};
const UserComponent = ({ queryReference }) => {
const data = usePreloadedQuery(query, queryReference);
return <h1>{data.user?.name}</h1>;
}
export default Component;
usePaginationFragmentOne of the advantages of using Relay, making your GraphQL API compatible with Relay, and following the GraphQL specification is that it makes it very easy to implement some features such as pagination.
The usePaginationFragment is a hook that can be used for rendering a fragment and paginate over it:
import { usePaginationFragment } from "react-relay/hooks";
usePaginationFragment(query, variables, options);
Here’s how the usePaginationFragment works:
fragment – A GraphQL fragment template literal. The GraphQL fragment must have a @connection and @refetchable directive, otherwise, it will throw an errorfragmentReference – A fragment reference that Relay uses from read data for the fragment from the storeWe can use the usePaginationFragment along with the usePreloadedQuery. Check out an example of usage of the usePreloadedQuery hook. First, we create our query and fragment:
const query = graphql`
query ProductsQuery($name: String!) {
...SearchProducts_products @arguments(name: $name)
}
`;
const fragment = graphql`
fragment SearchProducts_products on Query
@argumentDefinitions(
name: { type: "String" }
after: { type: "String" }
first: { type: "Int", defaultValue: 30 }
before: { type: "String" }
last: { type: "Int" }
)
@refetchable(queryName: "SearchProductsPaginationQuery") {
products(
name: $name
after: $after
first: $first
before: $before
last: $last
) @connection(key: "SearchProducts_products", filters: ["name"]) {
edges {
node {
_id
name
image
}
}
}
}
`;
Now, inside our component, we pass our query to the useQueryLoader hook and inside the child component we use the usePreloadedQuery:
import React, { useEffect } from "react";
import graphql from "babel-plugin-relay/macro";
import { useQueryLoader, usePreloadedQuery } from "react-relay/hooks";
const Component = () => {
const [queryReference, loadQuery, disposeQuery] = useQueryLoader(query);
useEffect(() => {
loadQuery({ name: name }, { fetchPolicy: "store-or-network" });
return () => {
disposeQuery();
};
}, [loadQuery, disposeQuery, name]);
return (
<React.Suspense fallback="Loading products...">
{queryReference != null ? (<ProductComponent queryReference={queryReference} />) : null}
</React.Suspense>
);
};
const ProductComponent = ({ queryReference }) => {
const query = usePreloadedQuery(query, queryReference);
const { data } = usePaginationFragment(fragment, query);
return (
<div>
{data.friends?.edges.map(({ node }) => (
<div>{node.name}</div>
))}
</div>
);
};
export default Component;
useMutationMutations are a very important part of GraphQL. It is what allows us to create, update, and delete data.
useMutation is the new hook for executing mutations with Relay. It is a very simple and straightforward hook, with only two parameters:
mutation — A GraphQL mutation template literalcommitMutationFn — A function that will be called in instead. This function is optional, most of the time you are not going to need ituseMutation returns only two values:
commitMutation — A function that will execute the mutationisInFlight — A value to check if the mutation is still in flight. You can use the commitMutation as many times as you want, so usually you can use multiple mutations in flight at onceCheck out an example of the usePreloadedQuery hook:
import React from "react";
import graphql from "babel-plugin-relay/macro";
import { useMutation } from "react-relay/hooks";
const mutation = graphql`
mutation SignInWithEmail($input: SignInWithEmailInput!) {
SignInWithEmail(input: $input) {
token
success
error
}
}
`;
const Component = () => {
const [commitMutation, isInFlight] = useMutation(mutation);
const onSubmit = () => {
commitMutation({
variables: {
input: {
email: email,
password: password,
},
},
onCompleted: ({ SignInWithEmail }) => {
if (SignInWithEmail?.error) {
return SignInWithEmail?.error;
}
updateToken(SignInWithEmail?.token);
},
});
};
return (
<form onSubmit={onSubmit}>
<input type="text" value={email} />
<input type="password" value={password} />
<button type="submit">Submit</button>
</form>
);
};
export default Component;
The newest version of Relay brought a set of new APIs that will help us to build more scalable React and GraphQL applications. The usage of React Hooks can help us to build more modular and high-performance applications, making our code easier to understand and free of unexpected side effects.
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

:has(), with examplesThe CSS :has() pseudo-class is a powerful new feature that lets you style parents, siblings, and more – writing cleaner, more dynamic CSS with less JavaScript.

Kombai AI converts Figma designs into clean, responsive frontend code. It helps developers build production-ready UIs faster while keeping design accuracy and code quality intact.

Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the October 22nd issue.

John Reilly discusses how software development has been changed by the innovations of AI: both the positives and the negatives.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now