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