Facing the Monster: An Analgesic for Relayphobia
Tags: relay, react, graphql, vite
Published: 2024-04-08 13:59:38

In all these years of building interfaces with React, I used Apollo whenever I needed to consume an API made with GraphQL, mainly because it's a library with a large community and numerous example projects

However, I recently joined the fantastic team at [Coinbase](https://www.coinbase.com/blog/rearchitecting-apps-for-scale) and met a dude I'd always heard people speak very poorly of: **The Frightening and Feared Relay**

Initially, I was really confused with this thing, having never worked with Relay before. Still, in a very short time, I was completely thrilled by the incredibly super fantastic DX this guy brings. I can make a powerful statement: undoubtedly, **it's one of the best tools for productivity within the React ecosystem.**

This raised a question for me: **Why on earth do people speak so poorly of Relay?** And why is its use much lower than other solutions for consuming GraphQL like Apollo?

Well, after discussing with some people, the reasons I heard the most were:

- Very confusing documentation
- Small community
- A few examples with TypeScript
- Very difficult to start using


My friend [Thayto](https://twitter.com/thayto_dev) mentioned this last point a few days ago. He wanted to start a project to explore Relay but found the documentation too confusing and overly theoretical. He wanted to start coding and learn along the way, but **the amount of configuration to do and the very few guides available made him give up**...

I admit that I was really sad to hear this. I wanted others to feel what I feel every day working with Relay

With that in mind, I decided to create a painkiller for all this Relayphobia: an extremely simple and detailed tutorial that can serve as a starting point for anyone who wants to learn Relay

**My goal here is not to teach Relay per se**, but to demystify the early stages of engagement with the tool and provide you the confidence needed to navigate and utilize the [official docs](https://relay.dev/docs/) more effectively

Alright, let's get started

## Starting the Project

I will use a Vite template with React and TypeScript as the base for the project.

> I am using pnpm here, but it's not a requirement; you can use yarn or another package manager.

```sh
pnpm create vite
```

Alright, having our project created, let's install the necessary dependencies:

```sh

pnpm add react-relay relay-runtime

```

And also the development dependencies:

```sh

pnpm add -D relay-compiler vite-plugin-relay graphql get-graphql-schema @types/react-relay @types/relay-runtime

```

I don't plan to go too deep into what each of these dependencies is, but to give a brief introduction:

- **react-relay**: This is basically the bridge between React and Relay, allowing React components to use data through Relay. 
- **relay-runtime**: This is the central core, providing the necessary infrastructure for Relay to function. It manages storage, data retrieval, and caching. 
- **relay-compiler**: This is where the magic happens! One of the most crucial and significant tools, it compiles the GraphQL queries used in the project in a very performant way. It ensures that these queries are optimized and converted into efficient formats. 
- **get-graphql-schema**: This CLI tool allows you to generate a GraphQL schema from a URL. It is an extremely necessary step.


## Schema and the Essential Configuration
Relay can work with any GraphQL API, but it's optimized for APIs that follow certain [conventions and standards](https://relay.dev/docs/guides/graphql-server-specification/). However, we don't need to worry about that for this guide

I've decided to use the [API from Rick and Morty](https://rickandmortyapi.com/documentation) because it's good and fun

With the URL in hand, we can generate the file that will be used as the schema:

```sh

pnpm get-graphql-schema https://rickandmortyapi.com/graphql > schema.graphql

```

With our schema created, we can create our configuration file at the root of the project:


```json
// relay.config.json

{
  "src": "./src",
  "schema": "./schema.graphql",
  "language": "typescript",
  "exclude": [
    "**/node_modules/**",
    "**/__mocks__/**",
    "**/__generated__/**"
  ],
  "eagerEsModules": true
}
```

We also need to add a plugin to our Vite configuration:

```ts
// vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import relay from "vite-plugin-relay";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), relay],
})
```

## Creating the Environment
IT'S ALMOST OVER, I PROMISE!!!!!

This is where it gets a bit complex. I confess that the first time I picked up Relay to study, I simply copied these files without even looking inside…

Basically, we are going to create a folder named relay with two files inside: `environment.ts` and `RelayEnvironment.tsx`:


```ts
// /src/relay/environment.ts

import {
  Store,
  RecordSource,
  Environment,
  Network,
  Observable,
} from "relay-runtime";
import type { FetchFunction, IEnvironment } from "relay-runtime";

const fetchFn: FetchFunction = (params, variables) => {
  const response = fetch("https://rickandmortyapi.com/graphql/", {
    method: "POST",
    headers: [["Content-Type", "application/json"]],
    body: JSON.stringify({
      query: params.text,
      variables,
    }),
  });

  return Observable.from(response.then((data) => data.json()));
};

export function createEnvironment(): IEnvironment {
  const network = Network.create(fetchFn);
  const store = new Store(new RecordSource());
  return new Environment({ store, network });
}
```


This guy is a bit confusing; let's go piece by piece:

- **Fetch Function (fetchFn)**: This is the simplest part. It's responsible for making POST requests to our GraphQL API.
- **Environment**: This class contains the central configuration of Relay. It brings together various configurations and provides the infrastructure for Relay operations.
- **Store and RecordSource**: The Store in Relay stores the fetched data. It acts like a client-side database. The RecordSource, on the other hand, is an in-memory storage. It's part of the Store that actually holds the data.


```tsx
// /src/relay/RelayEnvironment.tsx

import * as React from "react";
import { useMemo } from "react";
import { RelayEnvironmentProvider } from "react-relay";
import { createEnvironment } from "./environment";

export default function RelayEnvironment({
  children,
}: {
  children: React.ReactNode;
}): React.ReactElement {
  const environment = useMemo(() => {
    return createEnvironment();
  }, []);

  return (
    <RelayEnvironmentProvider environment={environment}>
      {children}
    </RelayEnvironmentProvider>
  );
}
```

In summary, `environment.ts` defines how data is fetched and managed, while `RelayEnvironment.tsx` provides the configured environment for our app

Adding the environment to the root of our app:

```tsx
// src/App.tsx

import { Suspense } from "react";
import RelayEnvironment from "./relay/RelayEnvironment";

function App() {
  return (
    <RelayEnvironment>
      <ErrorBoundary fallback={<h1>Something went wrong.</h1>}>
        <Suspense fallback={<p>loading</p>}>
          <h1>app</h1>
        </Suspense>
      </ErrorBoundary>
    </RelayEnvironment>
  );
}

export default App;
```


Now, enough with the configurations, for God's sake…

## Creating our first Query
Let's create a file named `HomePage.tsx`, which will be our main page and display a list of characters from the show


```tsx
import { useLazyLoadQuery, graphql } from "react-relay";

const homeQuery = graphql`
  query HomePageQuery {
    characters {
      results {
		id
        name
        species
        image
      }
    }
  }
`;

export default function Home() {
  const data = useLazyLoadQuery(homeQuery, {});

  return (
    <div>home</div>
  );
}


```


Here, we are essentially creating a query and calling it with the `useLazyLoadQuery` hook

However, if you check the type of our data variable, it will be **unknown**…

This is where the magic happens. Let's put our compiler to work!! 🧙🪄


```sh

pnpm relay-compiler

```

Based on our schema, the compiler will see that we are using a query and automatically generate a **file** containing all the types we need


![FileTree showing the generated file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccvetxcfuhe9dfxlz7ox.png)

_I use a light theme btw_

now, we can import the generated type and use it as a generic in the hook call:


```tsx
import { useLazyLoadQuery, graphql } from "react-relay";
import { HomePageQuery } from "./__generated__/HomePageQuery.graphql";

const homeQuery = graphql`
`;

export default function Home() {
  const data = useLazyLoadQuery<HomePageQuery>(homeQuery, {});

  // data.characters?.results;

  return (
    <div>home</div>
  );
}
```

now our response is typed, magical, isn't it?????

Let's display this data on the screen:

```tsx
  return (
    <ul>
      {characters?.map(
        (character) =>
          character && (
            <li key={character.id}>
              <p>{character.name}</p>
              {character.image && (
                <img src={character.image} alt={`Image of ${character.name}`} />
              )}
            </li>
          )
      )}
    </ul>
  );
```


now, just run the dev script and see the list of characters

```sh

pnpm dev

```

accessing **localhost:5173** (vite's default)


![Screenshot of the project running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/awyqxim7cz2xb694jo9h.png)



and that's it!!!!!! Relay configured and running 😎🤘

here's the [full code](https://github.com/mauvieira/relay-guide)

## Next Steps
As I mentioned in the beginning, this is just a guide to help you get started with Relay. I haven't covered API definitions or best practices - **now it's up to you**.

I'll leave you with a challenge: **Try to understand what fragments are**. In my opinion, this is the most fun part of Relay.

If you have any questions, feedbacks or simply want to chat, you can reach out to me on [twitter](https://twitter.com/_mauvieira) =)

see ya, take care 👋