Why you should Relay give GraphQL another look - WunderGraph
Jens Neuse
CEO & Co-Founder at WunderGraph
May 3, 2023·11min read
Last updated on September 9, 2025
Archive Notice
This article is archived and no longer maintained. It describes an earlier version of WunderGraph and its Relay integration, which is no longer part of the current product architecture. The examples and setup instructions may not work as described. For current documentation and guidance, see WunderGraph Cosmo.
State of GraphQL Federation 2026
How are teams governing schema changes, handling production traffic, and measuring Federation success? Share your experience and get early access to the full report. For every valid survey completed, we'll donate $30 to UNICEF.
What makes Relay so special?
Stop thinking for a moment about APIs from the perspective of the server. Instead, think about consuming APIs from the frontend perspective and how you can build scalable and maintainable applications. That's where Relay shines; that's where you can see a significant difference between Relay, REST, tRPC, and other technologies.
If you haven't used Relay before, you probably never realized how powerful GraphQL can be when combined with Relay. The next section will explain why.
At the same time, a lot of people are scared of Relay because it has a steep learning curve. There seems to be a sentiment that Relay is hard to set up and use, and that's true to some extent. It shouldn't take you PhDs to use it.
Every comparison of GraphQL vs whatever that fails to mention Relay is almost always useless. Even trpc comparisons completely fail to understand fragments. It's very easy to dismiss the complexity of GraphQL when you fail to understand the core value of the Query language.
Collocation of Data Requirements using Fragments
The typical data fetching pattern in applications like NextJS is to fetch data in the root component and pass it down to the child components. With a framework like tRPC, you define a procedure that fetches all the data you need for one page and pass it down to the children. Doing so, you implicitly define the data requirements for the component.
Let's say you've got a page that displays a list of blog posts, and each blog post has a list of comments.
In the root component, you'd fetch the blog posts with comments and pass the data down to the blog post component, which in turn passes the comments down to the comment component.
As an example, the Comment component has two data dependencies: title and content. Let's say we're using this component in 10 different places in our application. If we want to add a new field to the Comment component, e.g. author, we have to figure out all the places where we're using the Comment component, navigate to the root component, find the procedure that fetches the data, and add the new field to it.
You can see how this can quickly become a huge maintenance burden. The problem that leads to this is that we're fetching data top down. The result is tight coupling between the data fetching logic and the components.
With Relay and Fragments, we're able to collocate the data requirements with the component, while simultaneously decoupling the data fetching logic from the component. Together with data masking, this is a game-changer, because it allows us to build reusable components that are decoupled from the data fetching logic.
Re-Usable Components through Data Masking
Let's say we've got two sibling components that both use comment data. Both define their data requirements in a separate Fragment. One component only needs the title field, while the other component requires the author and content fields.
To prevent this, Relay allows us to mask the data before passing it to the component. If a component didn't define a field in its Fragment, it won't be able to access it, although it's theoretically available in the data.
To my knowledge, no other API client has this feature, which is why I think you shouldn't dismiss GraphQL without having tried Relay. GraphQL and Relay come at a cost if you compare it to tRPC. It's important to understand the benefits to make an informed decision on whether it's worth it.
Compile-Time GraphQL Validation & Security
Another benefit of using Relay is that the "Relay Compiler" (recently rewritten in Rust) compiles, validates, optimizes, and stores all GraphQL Operations at build time. With the right setup, we're able to completely "strip" the GraphQL API from the production environment. This is a huge benefit for security, because it's impossible to access the GraphQL API from the outside.
Moreover, we're able to validate all GraphQL Operations at build time. Expensive operations like normalization and validation are done at build time, reducing the overhead at runtime.
How does WunderGraph make using Relay easier?
Let's see how the integration with WunderGraph makes it easier to get started with Relay.
Setting up Relay + NextJS/Vite with WunderGraph is easy
We've tried to setup Relay with NextJS and Vite ourselves. It's not easy. In fact, it's rather complicated. We've built the necessary tooling to make Server-Side Rendering (SSR), Static Site Generation (SSG), and Render-as-you-fetch easy to use with any frontend framework.
Next, we need to configure the Relay Compiler to work with WunderGraph. As you'll see, WunderGraph and Relay are a match made in heaven. Both are built with the same principles in mind: Declarative, Type-Safe, Secure-by-default, Local-first.
Relay being the frontend counterpart to WunderGraph's backend. WunderGraph ingests one or more GraphQL & REST APIs and exposes them as a single GraphQL Schema, which we call the virtual Graph. Virtual, because we're not really exposing this GraphQL Schema to the outside world. Instead, we're printing it into a file to enable auto-completion in the IDE and to make it available to the Relay Compiler.
Relay Compiler Configuration with out-of-the-box support for persisted Operations
So, how do we wire up the Relay Compiler to work with WunderGraph?
As mentioned above, WunderGraph automatically persists all GraphQL Operations at build time. In order for this to work, we need to tell the Relay Compiler where to "store" the persisted Operations. On the other hand, Relay needs to know where to find the GraphQL Schema.
Server-Side Rendering (SSR) with NextJS, Relay and WunderGraph
Now that we've configured the Relay Compiler, we can start integrating Relay into our NextJS app, e.g. with Server-Side Rendering (SSR).
Render as you fetch with Vite, Relay and WunderGraph
Conclusion
Your key takeaway should be that GraphQL and Relay bring a lot of value to the table. Together with WunderGraph, you can build modern full-stack applications on top of three solid pillars:
- Collocation of components and data requirements
- Decoupled reusable components using Data Masking
- Compile-time Validation & Security
What's more, with this stack, you're not really limited to just GraphQL APIs and React. It's possible to use Relay with REST APIs, or even SOAP, and we're also not limited to React, as Relay is just a data-fetching library.
If you want to learn more about WunderGraph, check out the documentation.
Want to try out some examples?
One more thing. This is really just the beginning of our journey to make the power of GraphQL and Relay available to everyone. Stay in touch on Twitter or join our Discord Community to stay up to date, as we're soon going to launch something really exciting that will take this to the next level.