Facebook’s Relay Isn’t For Me Yet

Published on

Having worked with Relay more-or-less since it was released on a medium-sized in-house tool, i’ve concluded that despite solving a challenging problem in an elegant way, it’s not quite what I was looking for. To explain, I want to describe the path I took to the point where it appeared like Relay was exactly the solution I was looking for.

GraphQL provides clients (at the web server level or in the browser) with the ability to execute what was previously a sequence of API requests in a single step by writing a query that declares all the required data in the form of a graph traversal. A backend server interprets this query and returns the data. How this data retrieval is performed is left to us, since it typically depends on how your backend works. But it’s useful to note that the reference implementation combined with dataloader makes it pretty trivial to a relatively well-optimised set of sequential and parallel operations.

By itself GraphQL (and similar technologies like Falcor) is a pretty big win, and puts us in a better position that we were in before. Relay offers a much bigger leap forward by making it easy to define data-dependencies at the component level, whilst handling execution and caching for you. On the surface it looks like a big win, so what’s the problem?

For me it skips steps in the chain I was following, i.e. that of incrementally making it easier to build rich front-end apps. Remember that the problem I was looking to solve was how to handle data loading in universal (initial render on server, subsequent in the browser) websites, and in particular, multiple-page ones. This meant I had a few requirements:

Instead I think we’re going to benefit from exploring how to progressively build upon GraphQL, and leverage the tools we’re already using. You’ll notice how this leads us to something that looks a little like Relay, but should hopefully be accepted more due to it’s incremental nature. This list comes from a gist I posted, but I think Medium is a better environment for discussion, and i’ve changed it a bit anyway:

  1. Mimic Relay’s ability to compose queries from fragments declared against each component. There are libraries that do this, I even made a fairly crappy attempt at it in the months before Relay was released. We just need something clean, well-tested, that solves nothing more than this problem. This puts us in a land where the most immediately problem we had (straightforward data fetching in a universal web app) is solved

  2. Mutations. We need to be able to write data. So we need to make it easy to execute a GraphQL mutation. This is actually pretty straightforward, almost all of Relay’s complexity is surrounding optimistic updates and how to efficiently re-fetch all the data that may have changed. Optimistic updates are important, and may be a potential integration point with Flux.

  3. Navigation. If it’s possible to diff the GraphQL queries before and after navigation, then we may be able to only load the data we don’t already have. Relay solves this with caching, but we should explore whether there are other options (I’m suspicious about caching when I don’t have a proper invalidation caching).

  4. Instrumentation. If we do want integration with Flux or caching, we need to be able to add some extra things to the query (fetching ids and __typename as an example) transparently. We’ll have probably already worked with the AST in previous steps, so this may not be too difficult.

Essentially I believe we need some low-level APIs to handle things like query composition, query diffing (essentially building a patch query for navigation) and instrumentation. This could be implemented as a utility library. We can then explore building Relay-esque things on top of it.

Aside from continued turbulence surrounding how data flow should work in applications, the problem of handling backend data seems to be the last big one (unless you have a sense of what the next one is going to be, i’d love to know). Relay may ultimately end up being the solution, but it’s not there yet, and there’s still time for other solutions in this space.

Then hopefully we can build a decent framework and actually get some work done ;)