
React, Relay and GraphQL: Under the Hood of the Times Website Redesign - martgnz
https://open.nytimes.com/react-relay-and-graphql-under-the-hood-of-the-times-website-redesign-22fb62ea9764
======
notadoc
Putting aside all of the technical fun, from a pure reader perspective of
_any_ website sometimes I miss just simple boring HTML with a bit of CSS to
make it more pleasant and readable. I don't know about anyone else, but
particularly from a consumption standpoint I miss the days of 100k webpages.

I'm always stunned, but at the same time never surprised, when you discover a
single webpage is 35+ MB, consuming 2GB of RAM, and consuming CPU as if it
were a midrange video game.

~~~
pcurve
There is a lot of technology for technology sake to be found in American web
sites.

My favorite counter example is sankei news site.
[http://www.sankei.com/](http://www.sankei.com/)

All their pages end in .html.

Do view source and vast majority if byte is used for actual text content,
rather than javascript and markups.

Mobile friendly too, but you wouldn't know it because they disabled responsive
view unless it's visited from an actual phone. No premature mobile view with
hamburger kicking in when viewing it on desktop.

Comparison of View source between two sites is quite amusing.

view-source:[http://www.sankei.com/smp/](http://www.sankei.com/smp/)

view-source:[https://mobile.nytimes.com/](https://mobile.nytimes.com/)

~~~
eropple
_> but you wouldn't know it because they disabled responsive view unless it's
visited from an actual phone. No premature mobile view with hamburger kicking
in when viewing it on desktop_

Bug, not feature. If I'm viewing a site in a narrow window, I expect it to
collapse responsively. That site doesn't.

~~~
pcurve
I can assure you it's not a bug. Many Asian sites have fixed width on desktop.
They do not make their sites with 4-5 different media query break points.

Personally I prefer this because information is always in the same place
regardless of browser size. I don't have to worry about it moving around or
hiding.

~~~
eropple
Fine--misfeature. Bug in the spec. Having to squint and scroll sideways and
zoom is awful user-facing behavior.

------
colemorrison
I've looked at GraphQL a number of times. Does anyone have any practical
examples of integrating it with backend(s), APIs, and/or specific databases?

So instead of "we use GraphQL, much love" \+ basic example and how it looks on
React - a "here's how we take that structure and resolve it and return it."
Because that structure looks amazingly sweet - but if in the background it's
requiring circles of work, work and rework...

Anyhow, maybe I just don't understand it enough.

~~~
ruslan_talpa
You've hit a (pain) point. While graphql reduces the amount of trips to the
backend for the browser, those round trips get pushed down to a lower level,
between backend and db. The reference graphql-js implementation, while at
first looks so easy, you just write a resolver for a field, makes it oh so
easy to have terrible n+1 problems. dataloader helps a bit but it's not as
optimal as it can be.

You need to be very careful. To be optimal, you'll basically need to write
very complex resolvers that inspect the AST themselves and fetch the data
optimally which at that point is almost as writing your custom execution
module.

That is basically what i did, custom execution module to translate a graphql
request to a single sql query
([https://subzero.cloud/](https://subzero.cloud/))

~~~
scottmf
Which n+1 problems doesn't dataloader solve?

~~~
ruslan_talpa
It eliminates n+1 but not in a perfect way.

If you have a 3 level query (3 tables), the best you can hope for is to get 3
sequential queries, like get first level, collect the ids, request the second
level, collect the ids, get 3rd level. It gets even more complicated teh more
levels you and the bigger the dataset returned.

all of this can be done using a single join which is one roundtrip and it's
faster.

~~~
mambodog
If you have low latency queries (eg. because most things hit cache rather than
db) sequential queries aren't as much of a problem. Being able to join
everything into one query, on the other hand, is a luxury which can be hard to
maintain at scale.

In the 'join in SQL' case you could identify particular cases which are doing
sequential queries and implement a different loader which just does one. It's
not automatic but perhaps in most cases it's not necessary to do this step
anyway. In the worst case you're back to doing as much work as you would for a
bespoke API endpoint, but that's not the typical case. How much of a problem
this ends up being in practice very much depends on the type of app you're
building and how you intend to scale it.

~~~
ruslan_talpa
Saying join is a luxury is a dangerous thing :) (for impressional devs :)) 99%
of projects are not "at FB scale" :) so join is exactly the right thing to
use, it's been tuned over decades so until your scale/dataset does not outgrow
one box (and there are big boxes now), you are not going to do a better job
then the query optimiser (cause after all that is what you are trying to do).

~~~
andrewingram
I'd say build out your GraphQL server under the assumption that joins aren't
always an option, but allow them as an optimization where possible.

~~~
ruslan_talpa
why would i "optimise" for the thing that might never happen (join not an
option) while taking the hit right now? i've tested this (3 level query) and
the throughput is 10 times slower with dataloader, as in i need 10 servers
instead of one to do the same job.

~~~
andrewingram
I mean to structure your server in such a way where you're not trying to
automatically convert your entire query AST into a single massively
complicated SQL query. It's true that in simple cases, one complex query can
be the fastest way to get the data you need, but you leave this territory
pretty quickly.

It's perfectly valid to identify subtrees of your query that would benefit
from being executed as a database query, but to do that to your entire query
just sounds like you're asking for trouble, I'd even go so far as to call it a
premature optimization.

~~~
ruslan_talpa
I kind of understand why you'd think it's a massively complicated query. You
are probably thinking the types of joins on two tables on random columns with
weird conditions which go into full table scan.

I am talking about queries/joins between tables that have foreign keys between
them, like client/project/task/comment. I bet 90% of graphql schemas expose
those kinds of relations between types.

For those type of relations (with FK) i can generate a single query that is as
fast as it can be (certainly faster then dataloader) and as far as i've tested
(a few millions of rows in tables, 3-7 levels in a query) i didn't leave the
fast territory :) Of course there might be edge cases ...

About premature optimisations. Everyone likes to quote that, but never the
full one which is "We should forget about small efficiencies, say about 97% of
the time: premature optimization is the root of all evil."
[https://shreevatsa.wordpress.com/2008/05/16/premature-
optimi...](https://shreevatsa.wordpress.com/2008/05/16/premature-optimization-
is-the-root-of-all-evil/)

The paper where it was published was about using goto to optimize loops and
such small things, it was never "targeted" at algorithms and architecture.

As i think i mentioned here above, i was getting 10X throughput with joins
compared to dataloader and i would not call that premature.

~~~
andrewingram
I use a programming language that is 1-2 orders of magnitude slower than C,
should I switch today?

I get my data for a full UI within the budget i've allowed for uncached
scenarios (100ms, but I want to go to 50ms). The approach you're suggesting
will (in some circumstances) give me some short-terms win in terms of
throughput and response times, but you've not said anything to suggest that I
won't lose these benefits as I gradually transition into a domain-driven or
micro-services (yuck) architecture.

I like to build my GraphQL servers under the assumption of a domain-driven
architecture (because that's where all the projects I've worked on seem to end
up, your mileage may vary), and then shoe-horn in some short-term performance
tricks when I can.

I'm possibly a special snowflake here, but it's been a long time since i've
had the opportunity to work on a project where I can go straight to the DB. Be
it Elastic Search, a 3rd party, ill-advised micro-services, or complex logic
in-between storage and presentation; nothing has quite been a pure DB project
in the last 6-7 years.

Of course, you could argue this is premature architecture ;) but many of these
complexities are from day one, or at least pretty early in a project's life.

------
TekMol
Do you guys think it makes sense for a newspaper to use React for the
frontend?

I would think React might make sense for realtime dashboards and similar
webapps.

But does it make sense for displaying articles, navigation and ads?

~~~
jasim
Even if we don't use React on the client side, it can act as an excellent
server-side view templating language. This is because React inverts the
templating model on its head.

A typical template is an HTML file (or some variation of it) within which the
dynamic content is inserted using string interpolation.

A React view on the other hand is a piece of Javascript code which can compose
small snippets of view as JSX, use programming constructs like loops and
conditionals and finally return the assembled result.

Basically, React views are pure functions that return a validated HTML
snippet. Normal templates are big blobs of strings with logic mixed in.

~~~
daliwali
The string building approach is the fastest, and it's the benchmark to beat.
Any other abstraction is just piling more work on top and is generally just a
more inefficient way to output HTML. The most lightweight pseudo-DOM
implementations are still going to be significantly slower than string
concatenation, and I have benchmarks to back up that claim [0].

Realistically, a server-side rendered JS app is also going to run most of the
code that runs in the client per page load, so you would also have to consider
initialization costs as well. I've had to work on one that took 100+ ms to
render a static page (without accounting for network latency), which a static
file server could render the same page orders of magnitude faster.

Long story short, most of the newer, non-string based JS server-side rendering
does not consider performance a factor and consequently, perform pretty
terribly. There are band-aid fixes such as putting a reverse proxy in front or
running on super fast hardware, but it's like putting a band-aid on a bullet
wound.

[0]
[https://github.com/daliwali/simulacra/blob/master/benchmark/...](https://github.com/daliwali/simulacra/blob/master/benchmark/render.js)

~~~
jasim
I don't know who downvoted you or why, but yes - performance could be a
problem as you pointed out. I prefer the React model simply because it is more
programmer friendly. I would hope that performance is something that can be
fixed, or at least the 80-20 rule will come to help.

~~~
daliwali
Most JS server-side rendering never approaches anywhere near string
concatenation performance, and I've tried. Whatever abstraction you use has to
resemble string concatenation without doing much else, and it's really, really
hard to not do much else. That's why embedded JS templates (EJS) is so fast,
it just concatenates strings with some logic built-in to the template.

------
sAbakumoff
I tried Relay but found it ridiculously stupid that I need to change the
graphql API on my server in order to satisfy the relay concepts(universal id,
connections) so we rejected the idea and decided to go with the good old
fashioned redux

~~~
lioeters
I appreciate your perspective, as I've been considering whether to invest time
into learning GraphQL. It's informative to know that it's not suitable for
some use cases. Probably could have done without the inflammatory adjectives..
;)

As far as the criticism, would you be kind enough to explain what you mean by
"relay concepts", and how GraphQL failed to satisfy those needs?

~~~
djmashko2
I think the parent is saying something different: They were happy with GraphQL
for their API, but weren't happy with the requirements the Relay client
library imposed.

My interpretation is that they are now using a GraphQL API but fetching from
it with regular HTTP fetches and managing data with Redux on the frontend.

~~~
lioeters
Aha, thanks for the clarification. Upon reading the comment again, I see that
it's Relay that didn't suit his use case, not GraphQL.

------
roucoulawan
That's cool to have here a synthetic explanation on how Relay can be more
familiar.

I think I would just still be more attracted by Appollo framework which is
more a redux-like syntax, so more consistent with all the workflow of the apps
I used to develop. But maybe Relay has better benchmarks?

Also, if I want to stay REST but with optimist transactions between the
backend and frontend, I prefer lighter lib like
[https://github.com/tonyhb/tectonic](https://github.com/tonyhb/tectonic) or
even I just write some fast redux-saga watchers that helps to make my frontend
always synchronized when my app calls a mutating db request.

------
reconbot
This is very similar to the stack we use at BDG Media (bustle.com, romper.com,
etc) We use Amazon Lambda, GraphQL, a custom model layer with data loader and
redis, and preact. We haven't seen a clear benefit from Relay or Apollo with
out front end apps (tbd on our admin apps) but we have enjoyed the Relay spec
to help set server side conventions.

GraphQL has helped us make an api that is easy to understand, easy to change
and and easy to use. We love it.

~~~
dbbk
I thought Bustle was big on Ember and Ember FastBoot, has that changed?

~~~
reconbot
We still use Ember and fastboot on a few applications but it's being replaced
for the user side of bustle. I believe we did it because we were able to get
faster and smaller server side renders and a smaller js payload. I spend more
on the infrastructure side so I can't speak to the exact reasonings.

------
dmitriid
A website that should heavily depend on caching relies on tech that allows
only POST requests (non-cacheable, non-idempotent) and hopes for workarounds
later

~~~
djmashko2
GraphQL definitely allows GET and supports HTTP caching just as well as any
other kind of API!

~~~
dmitriid
GETs for GraphQL are awkward at best

The caching section in GraphQL docs is cringe worthy, and, as evidenced by the
article, that's exactly what Times are going to do: try to slap global ids
everywhere and pretend it's ok

------
pier25
Is anyone else here writing their GraphQL queries / REST calls in their
components?

IMO coupling the data layer with the presentation layer is a terrible idea.

~~~
scottmf
They're not writing queries/API calls inside components. They're defining the
components' data requirements alongside the components.

~~~
pier25
Now that I've paid more attention it seems I was wrong. They are actually
injecting the query into the route.

------
xiaoma
The sad irony of all of this is that I regularly view the NYT without
JavaScript because the experience is so much better without.

------
publicopinionsa
Why have you picked Relay over Apollo customer?

