GraphQL is the best thing about React / Relay / GraphQL

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at:, or follow me on Twitter.

React / Relay / GraphQL:

The big breakthrough here is obviously GraphQL. Not React or Relay, since there are many competing implementations that do similar things, but GraphQL. Even though there are graph query languages out there (Gremlin, etc) they were not suited to querying JSON over the wire. GraphQL is ideal. For my next project, I hope to do a Clojure implementation of most of GraphQL, because I think it can be married to Om.Next in a very powerful way.

Actually, I am not being fair. There are some interesting ideas that came out of the whole collection of technologies: React, Relay and GraphQL. Among those interesting ideas:

1.) Components should be mostly immutable (favor props over state)

2.) one way data flow

3.) Relay containers: the component and the query that gets data for that component should be married together, so a developer can easily see what data a component needs, and the query never goes out of date

4.) combine all queries at a higher level — the highest level container combines the queries from all the lower level containers, so you end up with one query. Relay also runs a diff on the info it has, versus the info it needs, so the query is always optimized for maximum efficiency. This has many advantages over the RESTful approach.

My criticisms of the system:

1.) this is a ridiculous among of boilerplate. I can hardly believe how much I have to write by hand. I do get that some of this is only because of the immaturity of the system. A year from now there will probably be command line tools that will give us some of the automation that we might expect from something like Ruby On Rails. Given a schema, I should not have to write, by hand, all of my Types and Connections and Models — much of that can be inferred.

2.) Mutations — in the middle of all of this beautiful Functional programming, they dredged up a classic Object Oriented pattern and they made it as painful as possible. The amount of boilerplate needed for mutations is really amazing. What should be 1 line of code takes 40 lines.

With all that said, I am 100% excited about GraphQL. It does seem to me very much a win over RESTful APIs, for all the reasons mentioned here:

I think I linked to Huey Petersen before, but his own views on this and Om.Next are very interesting:

Mutations in GraphQL are a bit of a mind change in that they include a payload which you can query.

mutation {
  addTodo(text: "Paint a self portrait") {
    todo {
    user {
      todos {

The above mutation both creates a todo for a user as well as return a payload with the user and todo it mutated which you can query. Relay takes advantage of this by querying for anything which a mutation changes that it is tracking in its local store. This ensures that as a client issues mutations it sees consistent data.

The facilities for configuring a mutation in Relay are non-trivial and the documentation is partial and sometimes wrong (I should submit a documentation PR instead of writing this sentence). I hang out in the Relay discord chat and its safe to say mutations have a learning curve. But once you do get beyond that learning curve they expose the tools required to keep data in sync. mutations are different in that they do not return data. Instead they are paired with reads in a transact. A simple transact might look like:

(om/transact! this `[(todo/toggle-status {:id ~id}) :completed?])

This transaction will both mutate a todo by toggling its :completed? value as well as reading that value back. So while the mutation itself returns no data the transaction as a whole is able to accomplish the same effect of mutating and reading at the same time.

In this transact we aren’t actually telling the transact! which todo the :completed? key toggled on. rolls with this by issuing a read for all :completed? props it is tracking. This may or may not be what you want, but you can get around this behavior by being more explicit with your transaction.

(om/transact! this `[(todo/toggle-status {:id ~id}) {[:todo/by-id ~id] [:completed?]}])

No ambiguity here.

Post external references

  1. 1
  2. 2