The Falcor data model is a graph, and the GraphQL data model is a tree.
By: Caleb Meredith
Tags: API, JavaScript, Nodejs, React, Software Development

Falcor

Out of the two Falcor was my first love. It’s existance was validation of an idea I had explored before. That idea being it is both possible and desireable to represent your entire data universe as a really big JSON object.

This idea was pretty seductive to me for a while. Just try this, If you squint hard enough, a well designed REST API easily looks like an extra layer on top of a really big JSON object. For example URLs like:

/people/1/friends

Could be easily represented in JSON:

{

"people": {

"1": {

"name": "John Doe",

"friends": […]

}

}

}

So that’s the mental model I’d use to build my REST APIs for a while.

Falcor takes this idea of your data being one big JSON object but throws away the old RESTful idea of a single-resource-to-URL mapping and therefore the requirement of multiple HTTP requests. Instead Falcor takes the large JSON object approach to data modelling and provides a cleaner, faster, and more declarative API.

But Falcor does keep the idea of a “path.” That’s how we are able to traverse our large JSON object, by paths. The path is as much Falcor’s query language as the GraphQL is well—GraphQL’s query language.

The second feature of Falcor that makes it akin to our REST APIs is the ability to have references. While Falcor represents your entire data as a large JSON object which is therefore inherently a tree, Falcor actually considers your data to be a “graph.” That’s what references allow.

So what’s even the difference between a tree and a graph? In academic terms, a tree is just a graph with no cycles. I know that makes lots of sense. The best way to understand the difference is visually. If you can’t see the two images below try to understand from the Wikipedia articles I linked earlier:

A tree. Note how there are no cycles.

A graph. Note how there are cycles.

JSON will always be a tree, that’s how the data format was designed. However, in order for Falcor to be awesome, the data needs to be a Graph. Falcor accomplishes this by references:

{

"friends": [

{ $ref: "/people/1" },

{ $ref: "/people/2" },

…

]

}

…and in REST world, a “ref” would just be a 303 redirect. This is why we say the data model of Falcor is a graph.

These two architecture constructs, the path and the reference, have serious positive benefits for Falcor.

The first is streaming. Because data is identified in Falcor with a unique JSON path, it is easy to merge the data in separate chunks. For example, a Falcor server could return a user object, which the UI could use to start rendering the UI. Then a little later Falcor could send down the user’s posts which would be used to continue rendering the API. By that point, however, the UI would already be responsive to user interactions.

The second is very similar, live updates. Similar to streaming, the path construct allows us to easily merge in live data from the server as it comes.

The third benefit you get from paths and references is caching. Most importantly you get caching and consistency without needing to define any special “Node” type or “id” field as GraphQL frameworks like Relay and Apollo must do. For Falcor, the “id” is just the path, and the data comes pre-normalized because of references. You don’t need the extra code of a framework for efficient data fetching with Falcor.

Vanilla GraphQL gets none of these benefits.

GraphQL

If you watch Laney Kueznel and Lee Byron’s talk at React Europe 2016 where they talk about both streaming and live updates (that we now know Falcor gets for virtually free) it is easy to see that GraphQL has fundamental problems on an abstraction level. Falcor is strictly a higher level abstraction.

So why can’t we use paths in GraphQL for smart merging strategies like in Falcor? The answer is in the sample query on the GraphQL homepage:

{

user(id: 5) {

id

name

}

}

…and then the response:

{

"user" : {

"id": 5,

"name": "Jing Chen"

}

}

In case you can’t see it, you can blame arguments. In the quest for a good developer experience GraphQL loses the ability to uniquely identify our data by paths like Falcor. This is why GraphQL needs the Relay Object Identification specification.

However, despite losing in the streaming/realtime arena, GraphQL wins big in practicality. Mutations, directives, fragments, and most importantly a type system give an incredible user experience to both small and large teams alike. All these things can be emulated in Falcor, but being first-class language features makes GraphQL much more practical for API use out of the box.

Conclusion

So which is best for your app? Falcor or GraphQL?

The popular story is that GraphQL is more complex thanks to its schema overhead and should therefore be used in more complex apps whereas Falcor is more general and should be used for simpler apps with faster iteration cycles. At least this is the advice Jafar Husain of Falcor gave. However, I don’t agree with his conclusions. I’m a big fan of Jafar and Falcor (in case you hadn’t already seen), but the question of “Falcor vs. GraphQL” doesn’t boil down to GraphQL’s type system because the types don’t necessarily add any extra complexity to the API definition. Rather the question is about the level of abstraction.

GraphQL has an introspectible type system, and as such it gives the average user much more for less. Therefore it is the ideal for apps where speed to market is important. GraphQL is also much more mature at the moment, and you can be guaranteed your API will still be in a semi-healthy state after a few months with little effort thanks to the constraints of GraphQL.

Falcor doesn’t have a type system making it more dynamic and less prescriptive. A more “pure” abstraction if you will. Therefore, Falcor is best used for teams who need a more flexible solution and aren’t afraid to build more themselves. Also, Falcor may be the best option for apps where data streaming and/or real time capabilities is a major part of the app (this may change in the future).

So in summary: