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,048