top | item 14142196

Relay Modern: Simpler, faster, more extensible

305 points| montogeek | 9 years ago |code.facebook.com | reply

108 comments

order
[+] marcosscriven|9 years ago|reply
Struck by this para:

"The native app teams discovered that using GraphQL came with the additional overhead of building queries by concatenating a bunch of strings and then uploading those queries over slow connections. These queries could sometimes grow into the tens of thousands of lines of GraphQL. Also, every mobile device running the same app was sending largely the same queries.

The teams realized that if the GraphQL queries instead were statically known — that is, they were not altered by runtime conditions — then they could be constructed once during development time and saved on the Facebook servers, and replaced in the mobile app with a tiny identifier. With this approach, the app sends the identifier along with some GraphQL variables, and the Facebook server knows which query to run. No more overhead, massively reduced network traffic, and much faster mobile apps."

Firstly, what query could be 10k lines? Secondly, if you're defining the query just by an id, how is that then any different to a fixed REST endpoint or stored procedure?

[+] voxmatt|9 years ago|reply
Exciting to see! I've been waiting on this since we decided to use Relay for our application about 6 months ago. Relay is amazing but quite an investment (especially mutations).

I'm a bit worried, however, that Relay Modern has focused a bit too much on the internal needs of a massive application like Facebook at the expense of fleshing out some of the rough spots of working with Relay.

Simpler, more explicit mutations is a wonderful improvement, as is more granular control over the cache, but there's no mention of subscriptions or client-side state control (using Redux on top of Relay is... doable, but not as elegant as one might hope for).

That all said, this is an impressive release and congratulations to the team. We're committed to Relay and hope this release grows the community.

[+] StreamBright|9 years ago|reply
Is there any great libraries to implement the backend for GraphQL? I think the benefits of using GraphQL on the frontend are pretty obvious, however last time I checked I had trouble of finding good documentation or implementation on how to serve GraphQL requests from the backend point of view.
[+] vning93|9 years ago|reply
Great question! There are great libraries like Create-GraphQL (https://github.com/lucasbento/create-graphql) that can help you scaffold an app on the server side pretty quickly, and it's fairly un-opinionated. That one in particular works with Mongo, but I believe they're rolling out support for other data sources as well soon.

In addition, if you're looking for a hi-fidelity way of building apps without having to worry about the server-side, Scaphold.io (https://scaphold.io) is a GraphQL backend as a service that can help. I work full-time here, and we help you get from zero to GraphQL in a matter of minutes.

And with this you have two options:

1) If you want to use the service, by all means :)

2) We're built to the open standard / Relay spec, meaning that if you want to create an app to learn about how the API is structured, that can help as well. Here's more of a primer on how our API is built and works anywhere (https://docs.scaphold.io/coredata/schema/).

Feel free to reach out if you have any questions on our Slack (http://slack.scaphold.io)!

[+] schickling|9 years ago|reply
For frontend developers who want to get started quickly, you can also use a hosted GraphQL service such as Graphcool: https://www.graph.cool

This approach abstracts all of the implementation details away and lets you focus on your GraphQL schema + implementing your business logic.

Disclaimer: I'm one of the founders of Graphcool. Happy to answer any kind of questions!

[+] WhitneyLand|9 years ago|reply
It seems many people loved GraphQL, a powerful and elegant concept, but then had a rude awakening with Relay as an overly complex, unwieldy, buzz kill.

Node, edge, and viewer are terrible mistakes w.r.t. naming and usability. I'm sure it's very intuitive for FB devs to think in these terms, but the words are specific to a problem domain and just don't translate as well to the general case as other choices might have.

[+] sgift|9 years ago|reply
That was one of the reasons I ran as fast as possible to Apollo after trying to understand Relay for a bit (that and more or less no tutorials for Relay). The other one was that Relay seems to be one of these "my way or the highway" frameworks, while I prefer libraries which follow my needs and don't force me to do all things the way they want.
[+] jankassens|9 years ago|reply
Hey, Jan here together with Lee from the Relay team. Happy to answer your questions.
[+] johntran|9 years ago|reply
1. Could you two do a Reactiflux Q&A?

2. Are people on the Relay OSS team willing to answer StackOverflow-type questions on a certain time?

- I would want to be able to programmatically run relay-compiler. Every time I update my client-side code, webpack detects the change and then runs relay-compiler.

- And get help figure out some errors related to mutations I'm encountering. I wouldn't want to post these type of things as a GitHub issue because it could technically not be a bug.

[+] willchen|9 years ago|reply
Congrats on the release. Could you summarize the pros and cons of picking Relay Modern vs Apollo Client?
[+] sAbakumoff|9 years ago|reply
Is it possible to use Relay along with Golang backend that serves graphQL queries via web socket?
[+] RaitoBezarius|9 years ago|reply
Thanks Jan, what are the remaining pain points to your opinion with the usage of Relay in a modern web app?
[+] weixiyen|9 years ago|reply
Thanks Jan. Will there be support for websockets?
[+] Meai|9 years ago|reply
Seriously, your own homegrown garbage collection inside the js runtime? I looked at React the first time it came out and aside from the insanity of using xml mixed with javascript or some kind of pseudo js, it was waaaay too complex. I dont know but it seems crazy to me to write applications like that, it makes xaml look decently simple.
[+] theprotocol|9 years ago|reply
I still firmly believe riot.js (http://riotjs.com/) is the best "react-like" tool even though it's almost entirely unknown (sadly) and its website/PR/general presentation is a bit janky.

It's essentially a very very tiny, minimally opinionated structural layer that lets you build html components (called "tags") using almost entirely vanilla JS. It inverts the JSX paradigm: where JSX is "html in the middle of your code", in riot the markup is primary and the code is a supplement to it (expressions in the markup via templates/mustaches, additional tag-specific script added outside of the markup if desired, tag-specific scoped css) so there's no JSX insanity.

It's like a much leaner react/vue, and frankly I love it. It's entirely minimal and you can bring in any library you want to use along with it (e.g. jquery for ajax, redux if you want...). It has virtually no cognitive load (just looking at a sample "tag" file for 2 minutes gives you ~80% of what you need to know), you just pick it up and work with it and just occasionally peek at the docs if needed.

I'm a huge fan of the "minimally opinionated" approach. The fewer idiosyncrasies and custom abstractions in tools, the more productive you are (I'm looking at you, angular!).

[+] andrewingram|9 years ago|reply
I feel you're being disingenuous here. They're just talking about automatic cache eviction, which wasn't possible/trivial with the previous version of Relay.
[+] yourad_io|9 years ago|reply
> homegrown garbage collection

You could say the same about shadow dom rendering: "Your own homegrown DOM renderer?". But in practice, it has a good reason to exist (performance vs. real DOM) and nearly zero usability issues (it is completely invisible to the developer, "it just works").

If they hadn't considered "garbage collection" (cache eviction, really) then it would have been seriously difficult to manage from outside Relay (and would partly defeat the point of using it).

[+] josephsavona|9 years ago|reply
Hey! I'm Joe and work on the Relay core team.

These are good questions: why does Relay Modern have garbage collection? Is that just a fancy name for cache eviction?

Let's put aside naming for a moment. Relay stores GraphQL data in normalized form, as a map of global identifiers to records. Each record has an identifier, type, and map of fields to values. Relationships between objects are expressed as fields that "link" to other records. These links are expressed as data structures - an object such as `{__ref: <id>}` - as opposed to direct references to the objects.

Using object references would mean that Relay could in theory let the JS runtime do garbage collection: except that the runtime would only see a cyclic graph of objects for which (typically) at least one root object had a persistent reference (the record corresponding to the root of the graph). In other words, it would do its job and retain all records in memory since they would all be (in)directly referenced from the root object, which would have to be referenced by Relay in order to access the data.

Relay, however, has more knowledge than the JS runtime does about how this data can be accessed: it can analyze the currently active queries against the object graph to determine which records are required to fulfill those queries. This is what the garbage collection feature does: remove records that may not be referenced by any active query.

Note that this has some aspects in common with standard garbage collection in programming language runtimes. There is a mapping of identifiers (memory addresses) to values. Each value may contain links (pointers) to other records (blocks of memory). Because the graph has cyclic references, standard cache eviction strategies - LIFO, LRU, etc - don't necessarily apply as they might evict data that is still referenced.

I hope this helps shed some light on this feature. Questions and suggestions (PRs) welcome!

[+] linkmotif|9 years ago|reply
This isn't JS runtime object garbage collection. As others have said, it's cache eviction. Bad naming, perhaps.

But if React is too much for you (tiny API surface, can learn in an afternoon), Relay will be way too much. I wouldn't bother reading more about them.

[+] _pmf_|9 years ago|reply
FB employs one of the world's best C++ developers (Andrei Alexandrescu) to write custom string implementations and maintain/extend their homegrown PHP VM. Their development practices would be completely unsustainable for any company that has anything resembling economic accountability.
[+] ianstormtaylor|9 years ago|reply
What are the differences between Apollo and Relay Modern at this point?

Why/when should you choose one over the other?

[+] voxmatt|9 years ago|reply
The learning curve for Apollo is a bit shallower for two reasons: (1) they have GraphQL libraries that allow you to implement a Relay-like client on top of an existing GraphQL end point (Relay requires some custom fields); and (2) better documentation.

We went with Relay, however, because it is, in my experience, more robust and more polished. The way that Relay handles fragment composition and defers component rendering, along with data masking, is much nicer and more... holistically considered(again, in my opinion). Using Apollo can feel a bit like patchwork sometimes.

Plus, ultimately, Relay is backed by Facebook and used in Facebook applications, in production, so it's not going anywhere.

[+] djmashko2|9 years ago|reply
Hi, I'm from the Apollo Client team.

We're going to write some more content in the coming days or weeks about the differences, but here are some of my main thoughts based on following along during Relay Modern development:

1. Relay uses a build process to generate code for queries. That allows some better performance optimizations and static typing out of the box. However, it prevents you from doing anything which requires arbitrary knowledge of the queries at runtime. It also means that if, for some reason, you can't use the build tooling, you can't use Relay. That's actually the original reason we started working on Apollo instead of using Relay ourselves. Apollo works with regular GraphQL ASTs at runtime, so you can use and write tools to work with those queries in any way you like. While it's not something all apps need, we've found some situations where this is desirable, especially for developers building companion libraries.

2. Relay doesn't have as many facilities for updating the store and working with mutation results. Apollo Client has a unique way to use GraphQL fragments and queries to read and write to/from the store, the most recent of which is described here: https://dev-blog.apollodata.com/apollo-clients-new-imperativ...

3. The Apollo Store is a plain JavaScript object, which means it can be easily serialized, persisted, hydrated, etc. So for example doing server-side rendering where you also hydrate the state is super simple in Apollo. Part of this is because of Apollo's Redux heritage.

4. Developer tools - we think it's super important to understand exactly what is going on with your data, both inside your app and across the wire. That's why in addition to sticking to simple plain objects we worked on some developer tools for chrome: https://dev-blog.apollodata.com/apollo-client-developer-tool...

5. One thing we're really proud of is how different libraries in the Apollo ecosystem are owned and maintained by different organizations from the community. This might make the experience of using it a bit less polished, but means that you can easily contribute or start your own projects if you need some non-standard features.

However, it's also great to remark on the similarities, which I think show that the community and Facebook are converging on some common good ideas. In fact, a lot of the initial decisions on Apollo are based on talking to the GraphQL team at facebook about their experiences:

1. Fully static queries - both Apollo and Relay encourage you to write your queries in the GraphQL language, and avoid manipulating them in unpredictable ways. This is actually one of the ways Apollo diverged from the original Relay release and it's great that it's coming together. Read more here: https://dev-blog.apollodata.com/5-benefits-of-static-graphql...

2. Colocation of data with the view - both Apollo and Relay enable you to do this. This pattern was one of the best achievements of the original versions of Relay, and we think putting the queries and fragments right next to the UI is a great pattern.

Most importantly, though, it's super encouraging that the GraphQL community is gaining another great tool. The best part about GraphQL is the diversity of approaches to servers, clients, and tooling, and that they can all work together through the specification. Really excited to see this release, and I hope we can all learn from each other and make GraphQL a real pleasure to work with.

[+] tonyhb|9 years ago|reply
> "Relay Modern is designed from the start to support garbage collection — that is, cache eviction — in which GraphQL data that is no longer used by any views can be removed from the cache"

Could we go ahead and implement proper caching as HTTP would do: expiration date per field/models, then eviction based on expiration dates? With an optional max cache size using LIFO, last used or the current model.

That way we don't need to refetch data if the API is set up to cache. It's sort of frustrating that staleness is just defacto ignored by UI right now.

[+] josephsavona|9 years ago|reply
Hi! I'm Joe from the Relay team.

Good questions! It isn't quite as straightforward as you might expect - the interconnected nature of graph-like data means that strategies that work for HTTP don't necessarily apply.

For example, storing per-field expiration times could incur additional memory overhead (you might end up with an object per field instead of per record). Storing per-record expiration times is tricky since the same record can have different fields fetched at different times. And a simple max cache size + LIFO/LRU/etc evication strategy means that the cache might evict a record that is still referenced by a view.

This type of TTL/expiration is something that we're continuing to explore.

[+] RaitoBezarius|9 years ago|reply
Correct me if I'm wrong :

But, wouldn't "garbage collection" solve the problem of many React web apps of consuming too much RAM (e.g. when using Redux, you never expire some keys and throughout the application lifecycle, you never cease of accumulating data inside your stores which makes RAM consumption goes up) ?

[+] ilovecaching|9 years ago|reply
My biggest gripe with the original Relay was that it didn't work with any GraphQl schema, but only those that provided a bunch of features like pagination and retrieving any object by id. This no batteries included, high initial bar for using Relay really turned me off of the product. I see that Relay Modern claims to be 'simpler', but I don't see anything about relaxing the constraints on my graphql schema.
[+] cainlevy|9 years ago|reply
I'd be interested to read an analysis of how this compares to the backends-for-frontends pattern.

Also, it seems like Relay Modern reintroduces API versioning, but automates it behind a compiler step. Is that a fair characterization? Does the server have to implement some kind of tracking and pruning for unused fragments, or is it expected that the fragments will accumulate at a non-threatening rate and never need pruning?

[+] mjmahone17|9 years ago|reply
There's just as much versioning for Relay Modern (from the server's point of view) as Relay Classic: so long as you have a client that sends a query string with old fields, your server needs to maintain support for returning those fields. So long as the field is nullable, you always can choose to "turn it off" server-side by always returning null.

In general, over time, as applications get more complex, the main queries you use will have more fragments. But we usually convert an entire query into a single ID (representing the entire query text, fragments and all). With each "version" of the query, fragments could be added, removed, or completely modified. Usually, your server shouldn't care what the specific fragment version is, but should be able to translate the fields the fragment asks for into a function that builds a response of that shape.

But one of the advantages of GraphQL in general (Relay included) is that you don't really need to version your API: so long as a field continues to exist in your schema, and continues to return the same type of object (even if that object adds new fields or interfaces), your old query/fragment will always receive the same shape. That may mean, over time, you "turn off" more and more fields by having them return as nulls to every client that asks for them, but you shouldn't think of it in terms of versions.

[+] foota|9 years ago|reply
I wouldn't say so, this has none of the maintenance implications that versioning does.
[+] Untit1ed|9 years ago|reply
Still can't see anything about making server-side rendering a first class citizen for relay :(.
[+] dasmoth|9 years ago|reply
I've been somewhat out of the Clojurescript loop recently, but this looks conceptually a lot like the om.next model ("colocated queries").

Makes me curious how many people are using om.next in anger. It's always seemed like a good idea (and now with some extra endorsement for the core principle), but judging by GitHub activity (which I realise isn't a perfect measure), the project seems to have rather lost momentum.

[+] schickling|9 years ago|reply
I'm super excited about this release! Great work Jan, Lee, Joe and everybody else who was working on this! :)

At Graphcool (https://www.graph.cool/) we were using Relay since the very beginning. It has enabled us to build frontend products at an incredible speed and while staying confident about the data layer. For instance our entire console is written using Relay at its core. (It's open-source btw: https://github.com/graphcool/console)

PS: We're also the authors of Learn Relay (https://www.learnrelay.org/) which we'll update to Modern Relay soon!

[+] darkmirage|9 years ago|reply
I work at Facebook and had the chance to use Relay Modern for an upcoming product. Was really happy with the performance and collocation of data and view just amazing. Love it :) BTW I realized where Relay's logo came from by chance while fiddling around with PowerPoint's new Morph transitions... https://gfycat.com/EnviousBothFinch
[+] pducks32|9 years ago|reply
Facebook has said that they never remove a property from their databases which seemed nuts to me but with Relay and GraphQL it makes a lot of sense. Why have the need for versioning when the client can request whatever they need.
[+] vidar|9 years ago|reply
Maybe I am way off base but "Colocation of data and view" reminds of awful PHP with SQL interspersed with HTML and CSS. Am I wrong?
[+] vning93|9 years ago|reply
Congrats on the release of Relay Modern guys! This has been a long awaited moment :)