
Optimizing
Applications
GraphQL



Ryan Chenkie
-
Angular/Node Consultant
@ryanchenkie

-
Google Developer Expert
-
angularcasts.io
GraphQL
Everywhere
is











What's not to like?


"Give me all posts and their data"
"Give me every post's author"



2012
Facebook Invents GraphQL
2015
GraphQL Spec Released
2017
graphql-js passes
50k daily downloads
Today
What can we do to optimize
GraphQL?
"Premature optimization is the root of all evil"
– Donald Knuth
Query
Batching

Totals
307 1,200 840
Customers
John Doe
Jane Doe
Upcoming Jobs
Website redesign
Modernize backend tech
/api/customers
/api/totals
/api/jobs



/graphql
200 OK
/graphql
200 OK
/graphql
200 OK

/graphql
200 OK
Grouped queries have problems
- All queries need to resolve before getting any data back
- Not easy to co-locate components with queries


10 ms
query {}
10 ms
10 ms
10 ms
10 ms
/graphql
200 OK
query {}
query {}
/graphql
200 OK
query {}
/graphql
200 OK
query {}
query {}
/graphql
200 OK
query {}
Batching is only as fast as the slowest query
!
Automatic
Persisted
Queries
query%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D
query%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D
query%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D
query%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D


query%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D
(to scale +/- 100m)
The larger the query string, the slower the network request will be
query%20%7B%0A%20%20customers%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%7D%0A%20%20totals%20%7B%0A%20%20%20%20jobsPending%0A%20%20%20%20jobsInProgress%0A%20%20%20%20jobsComplete%0A%20%20%7D%0A%20%20jobs%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%7D%0A%7D
C90929542A38E9B7DB7CFB409FF9AA0BF3469281CCEDDEB93F59561A65B33FB6
X
✓


C90929542A38E9B7...


???


query%20%7B%0A%...
X
✓
npm install apollo-link-persisted-queries


Apollo
Engine


Smart
Resolvers

Resolver
Resolver
Resolver
The n + 1 query problem

"Give me the posts for author 123"
"Give me all the comments for all the posts for author 123"

We can optimize the queries but this can make the API less flexible

Instant GraphQL on Postgres

Tanmai Gopal
What if we transform the GraphQL query into raw SQL?


GraphQL AST

What else can we do?
Performance
Perceived
Query Splitting



Optimistic UI

Save
✓
Results ?
Results ?
Results ?
Optimistic UI

Save
✓
Actual Results ✓
Actual Results ✓
Actual Results ✓
Final Thoughts
- Don't optimize too early
- Expect things to change
- Experiment

Thank You!
@ryanchenkie




Optimizing GraphQL Applications
By Ryan Chenkie
Optimizing GraphQL Applications
The promise of GraphQL is clear and compelling: allow your clients to ask for exactly the data they need at exactly the time they need it. Add the fact that you get a strongly-typed API and automatic introspection out-of-the-box and it’s easy to see why so many companies are making the switch to GraphQL. The default tooling that comes with GraphQL works well for small applications. However, once our apps start to get sizeable, it becomes easy to hit bottlenecks and performance degradations. Fortunately, there are several optimizations we can add without a ton of effort. In this talk, we’ll look at some of the best bang-for-buck GraphQL optimizations available. We’ll take a deep-dive into query batching to limit server requests. We’ll look at schema stitching to optimize how we call into multiple GraphQL endpoints. We’ll also talk about features offered by Apollo to improve perceived performance such as the optimistic UI pattern. Finally, we’ll look at some of the best ways to profile and monitor your GraphQL API.
- 3,290