Gentle introduction to Relay framework (React + GraphQL)
Tags: react, javascript, relay, graphql
Published: 2023-01-20 16:21:52

---
title: Gentle introduction to Relay framework (React + GraphQL)
published: true
description: Gentle introduction to Relay framework
tags: react, javascript, relay, graphql
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v5msfgzebefop94pgh74.png
# Use a ratio of 100:42 for best results.
---
## Table Of Content
* [Introduction](#introduction)
* [What is Relay?](#whatisrelay)
* [What is GraphQL?](#whatisgraphql)
* [Why GraphQL is better than Rest API?](#whygraphqlisbetter)
* [If GraphQL is better why Rest API is still here?](#whyrestapiishere)
* [Some frequent questions from beginners or prejudices](#faq)
* [Let's build React app with Relay](#letsbuild)
* [Structure of the project](#structure)
* [Creating Node.js http server](#server)
* [Adding GraphQL support into our server](#addinggraphqltoserver)
* [Creating React + Relay application](#client)
* [Generating schema.graphql file](#generatinggraphql)
* [Creating GraphQL query in our client app](#graphqlquery)
* [Update Component](#update-component)

## Introduction <a name="introduction"></a>

This post aims to gently introduce React developers to Relay framework. We'll start with a brief description of Relay and GraphQL (in simple terms) and then we'll create a simple React app that makes one simple query. This is a very beginner-friendly introduction so I intentionally avoid deep dives or advanced examples. Let's start!

### What is Relay? <a name="whatisrelay"></a>
In short, Relay is a framework that helps you to incorporate GraphQL query language into React applications.

### What is GraphQL? <a name="whatisgraphql"></a>
In simple terms, GraphQL is a better version of Rest API (this is the author's opinion and not an assertion). 

### Why GraphQL is better than Rest API? <a name="whygraphqlisbetter"></a>
For a number of reasons:
1. GraphQL has one endpoint instead of multiple. This helps frontend developers to develop application more quickly because there is no need to write new requests to backend or change old ones.
![oneendpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ieftwcgpi6hsbx1tf5xm.png)
2. GraphQL uses a schema that describes entities and their types. This schema is synced between the frontend and the backend. GraphQL also generates documentation that allows frontend developers to compose queries without the help of backend developers or other manually created documentation.
![graphqlschema](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/437vy51r7f5l61fftanz.png)
3. You can request only the needed fields of an entity and request a set of different entities as one request. Imagine you fetched a `book` entity which has an `authors` field with an array of author ids. Now you need to make another request to fetch information about authors by id. This will create a [waterfall problem](https://17.reactjs.org/docs/concurrent-mode-suspense.html#approach-1-fetch-on-render-not-using-suspense) in your React application. Relay and GraphQL handle this for you.

### If GraphQL is better why Rest API is still here? <a name="whyrestapiishere"></a>
Moving from Rest API to GraphQL is a quite complex process. First of all, frontend and backend developers need to learn it and be on the same page. Secondly, public APIs can't just move to GraphQL overnight because the API consumers still use Rest API clients. Although, [some public APIs can afford to have Rest API and GraphQL at the same time](https://docs.github.com/en/graphql). And if your API is not public facing, after some preparations, you can gradually adopt GraphQL [like it was done by Facebook](https://www.youtube.com/watch?v=KT3XKDBZW7M). Finally, it's just hard to replace such a fundamental approach as Rest API. Perhaps, GraphQL will just occupy its niche as it was in the case of WebSockets.

### Some frequent questions from beginners or prejudices <a name="faq"></a>
Q. Do you still need a state management library if you use Relay?
A. No, Relay can completely replace state management libraries such as Redux.
Q. Is Relay and GraphQL slower than Rest API?
A. Not as you can notice in the web application. There is some additional computational overhead regarding schema and typing system which could affect real-time applications or real-time game applications but for such applications, you wouldn't use JavaScript, let alone React, Relay, GraphQL...
Q. Does Relay (GraphQL) has Database requirements?
A. No, Relay (GraphQL) is agnostic to the data source.

## Let's build React app with Relay <a name="letsbuild"></a>

### Structure of the project <a name="structure"></a>
```bash
-- React-Relay-App
---- client (Create React App)
---- server (Node.js + http)
```

### Creating Node.js http server <a name="server"></a>
Let's start with the server. We will create a simple Node.js server that will have `GreetingsQuery` which will answer with `Hello World!`.

* [Install Node.js](https://nodejs.org/en/)

* Go to `React-Relay-App -> server` folder and initialize npm (you need it to install npm packages later).
```bash
cd React-Relay-App
cd server
npm init
```
* Then create `index.js` file and add the code for a simple http server that answers with status code `200` and `Hello World` text for now.
```javascript
// ./server/index.js

const http = require('http');

const hostname = '127.0.0.1';
const port = 5000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

* Launch created server by typing in the terminal:
```bash
node index
```
If everything is ok you should get this message:
```bash
Server running at http://127.0.0.1:5000/
```

* Then open `http://127.0.0.1:5000/` in the browser. Server should respond with `Hello World`.

### Adding GraphQL support into our server <a name="addinggraphqltoserver"></a>

* Install `graphql-http` npm package
```bash
npm i graphql-http
```

* Import all necessary assets to create GraphQL Schema into `index.js` file.
```javascript
// ./server/index.js

const { GraphQLSchema, GraphQLObjectType, GraphQLString } = require('graphql');
const { createHandler } = require('graphql-http/lib/use/node');
```

* Then add the code for the GraphQL schema that has a query called `GreetingsQuery` with a `greetings` field that answers `Hello World!`.

```javascript
// ./server/index.js

const schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'GreetingsQuery',
        fields: {
            greetings: {
                type: GraphQLString,
                resolve: () => 'Hello World!',
            },
        },
    }),
});
```

* After the schema is created we need to pass it to a handler.
```javascript
// ./server/index.js

const handler = createHandler({ schema });
```

* Then we can use this handler object in `http.createServer` function to handle all `/graphql` requests. Replace the previous content of `http.createServer` function with the following.
```javascript
// ./server/index.js

const server = http.createServer((req, res) => {
    if (req.url.startsWith('/graphql')) {
        res.setHeader('Access-Control-Allow-Origin', '*');
        res.setHeader('Access-Control-Request-Method', '*');
        res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
        res.setHeader('Access-Control-Allow-Headers', '*');
        if ( req.method === 'OPTIONS' ) {
            res.writeHead(200);
            res.end();
            return;
        }

        handler(req, res);
    } else {
        res.writeHead(200).end('Please, use /graphql suffix.');
    }
});
```
Ignore `res.setHeader` invocations since their purpose is to configure CORS and is not related to GraphQL directly.

* Final result of our server code
```javascript
// ./server/index.js

const http = require('http');
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = require('graphql');
const { createHandler } = require('graphql-http/lib/use/node');

const hostname = '127.0.0.1';
const port = 5000;

const schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'GreetingsQuery',
        fields: {
            greetings: {
                type: GraphQLString,
                resolve: () => 'Hello World!',
            },
        },
    }),
});

const handler = createHandler({ schema });

const server = http.createServer((req, res) => {
    if (req.url.startsWith('/graphql')) {
        res.setHeader('Access-Control-Allow-Origin', '*');
        res.setHeader('Access-Control-Request-Method', '*');
        res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
        res.setHeader('Access-Control-Allow-Headers', '*');
        if ( req.method === 'OPTIONS' ) {
            res.writeHead(200);
            res.end();
            return;
        }

        handler(req, res);
    } else {
        res.writeHead(200).end('Please, use /graphql suffix.');
    }
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

* Now let's re-start our server and open `http://127.0.0.1:5000/graphql` in the browser. You should get the following message:
```console
{"errors":[{"message":"Missing query"}]}
```
Which is completely ok. GraphQL expects valid query, you can't make a request to a endpoint and get response, like you would do with GET method in Rest API.

### Creating React + Relay application <a name="client"></a>
Now, since our server is ready to answer GraphQL queries, we can start with our client-side part.
I will use Create React App tool to generate React application, but the steps below can be applied to any other React web application or even React Native mobile application. 

* Go to `React-Relay-App` folder and create React project with the name `client`.
```cmd
cd React-Relay-App
npx create-react-app client
```

* After the generation process is finished, go to the `client` folder and install all necessary packages.
```cmd
cd client
npm i graphql react-relay relay-compiler babel-plugin-relay
```
**graphql** - the main GraphQL npm package
**react-relay** - the main Relay npm package
**relay-compiler** - goes through all your client files and parses GraphQL code
**babel-plugin-relay** - helps JavaScript to understand GraphQL code

* Next, create `relay.config.js` file in `client` folder
```javascript
// ./client/relay.config.js

module.exports = {
    src: "./src",
    language: "javascript",
    schema: "../data/schema.graphql",
    exclude: ["**/node_modules/**", "**/__mocks__/**", "**/__generated__/**"],
  }
```
`src` points to the folder where React components with GraphQL code will be located.
`schema` points to the shared between backend and frontend schema file. We will return to it later.

* Also, create `.babelrc` file in the `client` folder and add the following content.
```json
// ./client/.babelrc

{
  "plugins": [
    "relay"
  ]
}
```

* Finally, add the following script into `scripts` section of  `package.json` file.
```json
// ./client/package.json

  "scripts": {
    "relay": "relay-compiler"
  }
```
We will launch this script every time we add new GraphQL code to our React components. For simplicity, we will do it manually.

* Now let's launch Relay script that we added before.
```cmd
npm run relay
```
We will get the following error:
```cmd
 - The `schema` configured for project `default` does not exist at `../data/schema.graphql`.
```
That means that Relay on the client side expects `schema.graphql` from backend side to correctly validate GraphQL code in React components. In my opinion, this is the most important part of GraphQL because it completely elliminates misunderstandings between frontend and backend developers when using API and creates a strong contract between them.

Now let's go back to server and create a script that generates `schema.graphql` file.

### Generating schema.graphql file <a name="generatinggraphql"></a>

* First, let's create `data` folder in `React-Relay-App` folder. Out project structure should look like this:
```bash
-- React-Relay-App
---- client
---- server
---- data
```

* Then go into `server` folder and create `updateSchema.js` file with the following content.
```javascript
// ./server/updateSchema.js

const fs = require('fs');
const path = require('path');
const { schema } = require('./index');
const { printSchema } = require('graphql');

const schemaPath = path.resolve(__dirname, '../data/schema.graphql');

fs.writeFileSync(schemaPath, printSchema(schema));

console.log('Finished updating schema ' + schemaPath);
```
This will generate `schema.graphql` file in the `../data/schema.graphql` folder. 

* Now we need to export `schema` from our `index.js` file.

```javascript
// ./server/index.js

module.exports = {
    schema
}
```

* And let's add `update` script to `package.json` file of our server project:
```json
// ./server/package.json


"scripts": {
    "update": "node ./updateSchema"
}
```

* After that, run `updateSchema` script.
```cmd
npm run update
```
If everything is ok, then you will get the message:
```cmd
Finished updating schema \React-Relay-App\data\schema.graphql
```
And you can find generated `schema.graphql` file inside `data` folder with the following content:
```graphql
schema {
  query: GreetingsQuery
}

type GreetingsQuery {
  greetings: String
}
```

Now, it's time to go back to our client and launch `npm run relay` one more time.

### Creating GraphQL query in our client app <a name="graphqlquery"></a>

* Now that we have `schema.graphql` file in the `data` folder, let's go to `client` folder and run  `npm run relay`:
```cmd
cd client
npm run relay
```

* You should get the following message:
```cmd
> client@0.1.0 relay
> relay-compiler

[INFO] [default] compiling...
[INFO] [default] compiled documents: 0 reader, 0 normalization, 0 operation text
[INFO] Done.
```
That means `relay-compiler` went through all files in `client/src` folder and didn't find GraphQL code, so there is nothing to generate. Let's fix it!

* Create `relay` folder in `client/src`. Indside `relay` folder create `environment.js` file with the following content.
```jsx
// ./client/src/relay/environment.js

import React from 'react';
import {
    Store,
    RecordSource,
    Environment,
    Network,
    Observable,
} from "relay-runtime";
import { RelayEnvironmentProvider } from "react-relay";

const fetchFn = (params, variables) => {
    const response = fetch("http://localhost:5000/graphql", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
            query: params.text,
            variables,
        }),
    });

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

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

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

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

```
Don't focus too much on these settings as they are made only once at the beginning of a project. What it does is creates wrapper around `fetch` and then passes it to Relay library which handles network requests. Then we create `RelayEnvironment` provider to use it as wrapper around our root `App` component.

* Now, let's go to `client/src/index.js` file and wrapp out React application with `RelayEnvironmentProvider`:
```jsx
// ./client/src/relay/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import RelayEnvironment from './relay/environment';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RelayEnvironment>
      <React.Suspense fallback="Loading...">
        <App />
      </React.Suspense>
    </RelayEnvironment>
  </React.StrictMode>
);
```
Notice, that we use `React Suspense` here which [is a must when you use Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/).

* We're almost done. Let's go to `App` component and create GraphQL request. We create `AppQuery` outside `App` component.
```jsx
// ./client/src/App.jsx

import graphql from 'babel-plugin-relay/macro';

const AppQuery = graphql`
  query AppQuery {
    greetings,
  }
`;

function App() {
...
```
Relay enforces several rules, one is that we must start the name of a query with a component's name, so result should be `AppQuery`.

And then inside `App` component we use hook `useLazyLoadQuery` to make a query.
```jsx
// ./client/src/App.jsx

function App() {
  const data = useLazyLoadQuery(
    AppQuery,
    {},
  );
...
```

After that we render `data` result in our `App` component as `{data.greetings}`. The full `App` component should look like this:
```jsx
// ./client/src/App.jsx

import { useLazyLoadQuery } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
import logo from './logo.svg';
import './App.css';

const AppQuery = graphql`
  query AppQuery {
    greetings,
  }
`;

function App() {
  const data = useLazyLoadQuery(
    AppQuery,
    {},
  );

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          <code>{data.greetings}</code>
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
        and 
        <a
          className="App-link"
          href="https://relay.dev/docs/"
          target="_blank"
          rel="noopener noreferrer"
        >
          Relay
        </a>
      </header>
    </div>
  );
}

export default App;

```

* Ok, we added GraphQL query. Now we can start the app and see results! Not so fast! After adding GraphQL code into React components we should run `npm run relay` so it can autogenerate needed code for runtime.
```cmd
npm run relay
> client@0.1.0 relay
> relay-compiler

[INFO] [default] compiling...
[INFO] [default] compiled documents: 1 reader, 1 normalization, 1 operation text
[INFO] Done.
``` 

Notice that inside `src` folder the folder name `__generated__` appeared. That's the runtime code that Relay is needed to function correctly. You don't need to look inside.

* Now is the time to start server and client and see results!
![results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/svj0smg2j2zxfj2zmoeb.png)

Congratulations! We did it! I hope now you see why I choose to make this example as easy as possible. Even given that, the post turned out to be quite big. 
Anyway, that's just 10% of the capabilities of Relay framework and GraphQL and ahead of us are mutations, arguments, variables, fragments and many more. 

Links:
1. [Repo with the code](https://github.com/RinatRezyapov/Dev-To-React-Relay-App)
2. [Official GraphQL Docs](https://graphql.org/)
3. [Official Relay Docs](https://relay.dev/docs/)
4. [Building The New Facebook With React and Relay talk](https://www.youtube.com/watch?v=KT3XKDBZW7M)