Using GraphQL Fragments in Relay: co-locating fragment spreads
My company’s codebase uses a lot of ESLint rules, and when I first started using the GraphQL client Relay two in particular kept tripping me up - unused-fields
and must-colocate-fragment-spreads
. These are used to help enforce usage of GraphQL Fragments.
Ideally, you would be using fragments to begin with so you don’t run into these ESLint errors. But when I first started with GraphQL, my knowledge was pretty basic: you write a query, you get your data, and then you’re done. I didn’t know much about fragments, and really, they felt like more hassle than they were worth. I had only used REST endpoints up to this point, so I would always feel like, “Just give me my data! Why do I have to write this extra fragment code?”
But after I properly wrapped my head around it, and understood why they are useful, I think fragments are not so bad. And hopefully you’ll have a better understanding too after reading this post.
Fragments 101
To start things off, let’s say you have a GraphQL query and React component structure that looks something like this:
For a developer not familiar with GraphQL, this is pretty standard. You fetch all your data, then pass it down as props to your child components.
However if you have the eslint-plugin-relay plugin setup, you’ll run into an unused-fields
linting error. We are querying for the profiles
value here, but not actually using it. (Passing it in as a prop doesn’t count as using it).
Define a fragment inside of the file that uses the values
So to fix this ESLint error, we’ll need to move any values that aren’t needed into the actual component where it’s used, and use a fragment. This is known as “fragment colocation”:
If you look at the first line of the fragment, fragment ourNewFragment on UserProfiles
, you might be wondering, what is that on UserProfiles
bit? This is the GraphQL type of the data we are fetching. If you take a look at your GraphQL schema, you should be able to figure out what the correct type should be. You should have access to a GraphQL explorer UI in your local environment somewhere where you can play around with queries and look at the GraphQL schema, so I recommend using that if you haven’t already.
Spread the fragment in the parent data-fetching file
Now that you have a fragment, you spread it on the original GraphQL query:
Pass down a fragment reference to the child component with the fragment
Finally, we need to pass down a reference from the file where we are doing the GraphQL query, to the file where we have defined the fragment. This way Relay has a link between the fragment and the query it is spread in:
And you’re done!
A side note on ... on
and union types
In the codebase you may come across the ... on
syntax. e.g.
If you’re new to GraphQL - the ... on
spread is used when you have union types. In the above example, userProfiles
can return either the type UserProfiles
or a QueryError
.
I used to get quite confused by Relay fragments, and I think the union syntax was one of the key reasons why. When I looked at a GraphQL query like this:
My brain would misinterpret this as me needing to pass in something like userProfiles.UserProfiles.ourNewFragment
to the child component, and I would get confused on what I was supposed to pass in as a reference.
If you can wrap your head around the union type syntax in GraphQL I think you should be fine - just remember you only need to pass in the userProfiles
bit.
Why use fragments?
So when you think about the hassle of breaking down your GraphQL query into fragments and passing down the refs, it can be tempting just to shush this ESLint rule with an eslint-ignore
. Especially when you don’t really know why ESLint is giving you this error, it just feels like an extra piece of busywork that you don’t really have the time for.
Preventing unused fields
But Relay fragments are useful in keeping a cleaner codebase. One of the main benefits of fragment colocation is that it prevents unused fields from being unnecessarily fetched. Going back to our previous example, imagine at a later stage we decided to just delete the UserDashboard
component altogether.
If we weren’t using fragments, we could easily forget to remove the profiles
value from the GraphQL query, and so it would just sit there forever. Not a big deal on its own, but the bigger your codebase gets, the more of these might end up floating around. So by enforcing fragment colocation and making sure all your fields are used, we won’t run into this problem.
On passing down fragment refs
Another doubt I had with fragments was that although it’s supposed to be the better solution, it still doesn’t feel that clean, especially when I’m passing down the fragment refs through multiple component levels. Aren’t we just prop drilling?
And yeah, I guess to an extent. But you can justify it as the least annoying form of prop drilling. Suppose we didn’t have fragments, and these 3 components were each in separate files:
Removing the icon
value, would require changes to all 3 components and files.
If we were using a fragment in the Name component and passing down a ref instead:
You would only have to change what the fragment is fetching in the Name
component, but be able to skip changes to your other files. So not as bad.
Hope this was useful!