What is Relay?
If you look at relay.dev, Relay is the GraphQL client that scales with you. This definition is simple and defines Relay pretty well for the ones that already know all the features that Relay brings to the table.
For the ones that still having used Relay or still do not understand all the benefits of using Relay, this article provides practical examples of how Woovi is using Relay to scale their backend and frontend.
Relay Architecture
Relay is composed by many packages. Understanding each package, can help you understand how Relay works.
Relay Compiler
Relay Compiler will read all GraphQL operations (queries, mutations, subscriptions), and fragments. It will parse, transform and optimize.
You can play with some Relay Compiler transforms here Relay Compiler Repl.
There is also a new Relay Compiler Explorer
Relay Compiler also polyfill GraphQL fragments to enable variables at fragment level, as GraphQL still do not support variables at fragment level yet.
Relay Compiler also ensure everything is correct, you won't be able to send a broke query to production, it won't even compile.
The output of this optimization uses AST, and it is stored on __generated__
folders. You can explore GraphQL AST using AST Explorer.
babel-plugin-relay
Relay provides a babel plugin that replaces the graphql
tag to require the artifact generated static query generated by Relay Compiler.
Here is an example of this transformation
const user = useFragment(
graphql`
fragment App_user on User {
app
}
`,
props.user,
);
Transforms into
const user = useFragment(require('./__generated__/App_user.graphql.ts'),
props.user,
);
Relay Test Utils
We use Relay Test Utils in our integration tests at frontend. It makes it pretty easy to mock Relay environment and any GraphQL query or field.
Below is an example of a mock resolver that tells relay test utils to mock every User
to have a name Woovi
. I don't need to mock the whole GraphQL operation, just the fields that make sense for my test case. It reduces a lot the number of fixtures.
const mockResolver = {
User: () => ({ name: 'Woovi'}),
};
Relay Runtime
Relay Runtime is the core part of Relay. It handles how Relay will do the requests (network layer), it will says how Relay will store the data. It also handles mutations and subscriptions.
If you wanna port Relay to angular or vue, you just take the Relay Runtime and add your favorite framework on top of it.
React Relay
It is the React bindings for Relay Runtime.
It evolved a lot since Relay Classic, to Relay Modern, and now Relay hooks with support to Suspense and render as you fetch.
Relay hooks provides the best DX. useFragment
let you declare what data your components needs, and Relay will figure it out how to bring this data to that component, from the store? from the network? you don't need to care about it.
useMutation
will execute GraphQL Mutations and will also ensure you don't double execute them, so you don't need to worry when an user clicks the form button twice or more.
Partial Rendering
One of the Relay unique feature is to partial render your components based on existing data in your store.
<>
<PostTitle post={post} />
<Suspense>
<PostComments post={post} />
</Suspense>
</>
Imagine that you already have post.title
in your Relay store, but you don't have post.comments
.
Relay will be able to render <PostTitle />
component, and it will suspend <PostComments />
until it gets the post comments. This provides a faster first render experience, and better UX for our users.
Relay at Backend
We also use Relay at Backend.
We use Relay Compiler to compile and generate types for queries/mutations/subscriptions of our tests in the backend. This workflow provides typesafe testing and a better DX and asserting resulting of GraphQL tests.
Here is some code showing how to use it:
const graphql = (strings: TemplateStringsArray) => {
if (strings?.schema) {
throw new Error('use graphqlExecute');
}
return strings[0];
};
export const graphqlExecute = async <T extends OperationType>(
args: GraphQLArgs<T['variables']>,
) => {
const result = await graphql(args);
return result as unknown as ExecutionResult<T['response']>;
};
const source = graphql`
query AppQuery($id: ID) {
user: node(id: $id) {
... on User {
name
}
}
}
`;
const result = await graphqlExecute<AppQuery>({
schema,
source,
rootValue,
contextValue,
variableValues,
});
AppQuery
type is generated by Relay Compiler, and result
has the correct type of it, if you change the query, it will change the result
type. It is a great DX.
More Relay at Woovi
We are using more and more Relay at Woovi. Relay let us decouple our components. It lets us easily refactor without breaking anything. Relay Compiler in rust is faster than ever.
In Conclusion
My first contribution to Relay was December 15th, 2015, check it here https://github.com/facebookarchive/relay-starter-kit/commits?author=sibelius.
Since there, GraphQL and Relay evolved a lot. Relay has a step learning curve, because it requires you to do the right thing, not the easiest thing. Relay requires a new mental model of how to deal with data in components.
We are going to keep using more Relay at Woovi to help us scale.
Relay Workshop
If you want to learn Relay, the best resource is Relay Workshop. It has 12 practical exercises to learn the most used Relay features.
Woovi
Woovi is a Startup that enables shoppers to pay as they like. To make this possible, Woovi provides instant payment solutions for merchants to accept orders.
If you want to work with us, we are hiring!
Top comments (0)