A GraphQL deep dive: When fragments and React components join forces
Part 1: Conduit’s technical series on using GraphQL in production
You know of the buzz surrounding GraphQL, and it sounds exciting. It’s a Facebook invention that turns the traditional REST paradigm on its head, eliminating excessive client-server round trips and unnecessary data transfer with a funky, JSON-but-not-really syntax.
But while there are a lot of simple GraphQL examples, tutorials, and musings out there, there’s not much surrounding the process and challenge of using GraphQL in production. For us, that made choosing — and implementing — GraphQL harder than it needed to be. We learned a lot.
From Conduit, this is an engineering series on GraphQL with a goal most tutorials don’t tackle: the design, implementation, and optimization of APIs that serve real data to real applications. It’s still from a particular perspective — our perspective, which, like any other, comes with compromises and imperfection — but we hope to be one of this growing community’s strongest advocates.
This is a multi-part series, with subsequent articles coming soon:
- (This article) On the client: GraphQL fragments and React components, and when GraphQL does (or doesn’t) make sense
- On the server: Authentication and authorization models that make sense, as well as best practices for security
- On the server: GraphQL and relational databases (PostgreSQL), minimizing server-DB round trips, and more
- On the server: Rapid and durable schema design, filtering gotchas, and validating data before it comes in with custom scalar types
- And more, by request and as we learn :)
Let the client pick and choose data
One of the biggest benefits of GraphQL is that clients get the power to pick and choose data. Instead of a single API endpoint that arbitrarily sends a blob of information, we can control each and every field. Even though we front-load effort to write our GraphQL server, we’ve essentially made infinite API endpoints that fit our every need.
One way we take advantage of this at Conduit is by logically partnering GraphQL fragments and React components. Their marriage gives us strength in flexibility and organization, and is an excellent case study into the powers of GraphQL.
What’s a fragment?
One of the lesser-known features of GraphQL is the fragment. Designed like the ES6 spread operator, it allows us to request the same set of fields when they’re organized the same way.
For example, if we want to grab lists of people — one of Boston-based developers, and one of SF-based salespeople — and the last three times you grabbed drinks with them, we’d write the following query:
(By the way, save for a few clarity abstractions, all examples in this series actually work and run in the Conduit ecosystem. It’s the real deal!)
We’ve duplicated a lot of code here and, over time, this will be a hassle to maintain. We can instead write a fragment to package the fields—including any aliases we make and any relationships we grab — all together:
If you’re using Webpack, this fragment can also go in a separate
.graphql file and be imported at the top of another .graphql file with
Hey, React components!
As it turns out, this kind of abstraction should ring a bell: it’s similar to the way that React components encapsulate behavior and rendering.
In the same way that a parent React component doesn’t care about the child component or what it renders, a “parent” GraphQL field doesn’t care about the fragment: what fields or relationships it asks for.
We take advantage of this property by establishing one-to-one relationships between React components and GraphQL fragments. We can write fragments that define exactly what data a component needs:
For clarity, we name the fragment as the component and even put both files in the same folder:
Using the fragment and component together
When it’s time to use this component — say, in a list of contacts — we write a parent
ContactItemList component that will render a list of
ContactItem’s, and we write a parent
ContactItemList GraphQL query that fetches a list of
Now the dependencies are easy to follow, down to the directory structure:
GraphQL: ContactItemList.gql > ContactItem.gql
React: ContactItemList.jsx > ContactItem.jsx
Peace of mind
From this setup, we reap numerous benefits and take maximum advantage of what React and GraphQL have to offer.
First, we have a clean one-to-one relationship between GraphQL and React, while still managing to isolate data handling away from the actual component. For any child component —for example,
ContactItem—the parent doesn’t care at all where or how they are being used!
As long as the parent uses the accompanying GraphQL fragment, a component ensures that it has all the data it needs to display properly, down to any complex rendering it might do. And if components are nested within other components (as they often are), we can nest fragments inside other fragments.
This is the best type of validation because we guarantee that the data coming in is always shaped and formatted in a particular way. Not even PropTypes can make this guarantee.
Decrease how many parts to change
Replacing your car floormat shouldn’t involve disassembling the engine. But REST will often force developers to do just that.
Say that we’ve updated our
ContactItem component to also include the contact’s phone number. Let’s compare how we could make this change without GraphQL — that is, under a traditional REST API architecture.
If you have a REST endpoint that matches the view exactly, you’d need to dive into your API code and add the phone number to that endpoint. If your project is big enough, you might have to make those changes in a separate Git repository and even wait to deploy to the server.
Alternatively, you may having generic endpoints that include all the fields already, even if they’re not relevant. But that’s a lot of bandwidth wasted for you and for your users. All this time it has been sent to the client just to be discarded.
Either way, your project faces a lot of back-end change for what is really a front-end decision. There are a lot of places where things could go wrong or get out of sync. Adding the phone number could have been a decision made on a whim, or even an A/B test delivered to just some of your users. Is it worth it to permanently change your endpoint?
With GraphQL, this isn’t a problem anymore. You would have written your endpoint to handle the field already, so all that’s needed is to add it to the parent query. With one-to-one fragments and components, this is an even cleaner change. Our update won’t even leave the file directory!
When does GraphQL make sense?
Of course, all of this is really neat. But there’s no free lunch. With GraphQL, you gain immense amounts of UI and front-end flexibility at the cost of designing a back-end that’s durable and flexible enough to withstand any query a client throws at it.
What if a client crafts a malicious query that takes forever to fetch? Or which breaks out of their bubble and touches another user’s private data? With GraphQL, you have the same security and durability concerns of a REST API, but now have to deal with them on the scale of, well, infinite API endpoints. Its strength can become your Achilles’ heel.
The rest of Conduit’s series will focus on the server side of GraphQL, with various security, performance, and development optimizations we’ve done to improve our product’s (and our engineers!) ability to process and serve a tremendous amount of data.
But to help you make your decision to go — or forego — GraphQL, here are some considerations to make:
“Circular” requests → Graph-like data → GraphQL
GraphQL excels with graph-like data. If you’re making “circular” requests — from one type to another, and then back — your data is graph-like and you’ll likely enjoy GraphQL.
For example, we regularly fetch contacts and all the interactions that a user has had with those contacts. We also fetch interactions and the contacts connected to those interactions. To us, supporting the former is just as important as supporting the latter — our data is graph-like.
If you were running a blog, you’d fetch authors, then posts, and then comments. It would be unlikely — although possible — that you’d ever start with a single comment and find all of the related posts, since there is only one. This data is not very graph-like.
Rapid front-end iteration → GraphQL
If your front-end moves fast, you’ll appreciate the nimbleness of GraphQL. You won’t need to wait for an API to catch up. At Conduit we can test prototypes and spin up new views and components nearly instantly, answering “what if” questions on the spot.
GraphQL comes with remarkably well-designed developer tooling too, including GraphiQL (here’s a Star Wars example), an in-browser explorer and tester for your queries. It uses your in-code documentation to automatically offer suggestions and autocomplete and can make API development a breeze.
Why is Conduit using GraphQL?
Conduit (by the way, sign up for our waiting list!) is personal relationship management software for professional and power networkers. It’s like a data-driven personal CRM, offering a birds-eye view of relationships and interactions in your life — as well as a deep dive into each connections, with detailed history, external sync, intelligent reminders, action triggers, and more.
Driving all of this is the Conduit Personal Graph, our graph model of a person’s relationships and activity — automatically synced from all your services, organized intelligently, and always learning. It’s the most comprehensive of its kind. And it’s accessed by — you guessed it! — GraphQL.
We’ve been all-in on GraphQL as our API standard since the beginning. Along the way, building Conduit and the Personal Graph has given us tremendous insight into actually using GraphQL in the real world — in production and at scale. It’s certainly forced us to tackle problems that aren’t written about in simple tutorials, and at times, to work around pain points.
We’re a small company, but we’ve proudly joined the pack of companies using GraphQL in production — and we want to share some of our experiences back with the developer community. We’re always learning, so reach out to continue the discussion. (We’re hiring in Boston, too!)
Next / Coming soon:
A deep dive into GraphQL: Authentication and authorization models that make sense, as well as best practices for security