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