OT: Daddy! It's the Best thing since sliced bread!
is a phrase my 5 year old son keeps repeating to me these days.
This post is to explain the concept and merits of compiled GraphQL from a web developer's perspective.
Once upon a time there was a Client that never changed, it always made the same request to the server. Server in turn optimised the Query to the DataStore to fetch it efficiently and return it.
┌──────┐ ┌──────┐ ┌─────────┐
│Client│ │Server│ │Datastore│
└──┬───┘ └──┬───┘ └────┬────┘
│ Request1 │ │
│ ─ ─ ─ ─ ─ ─ ─ ─>| │
│ │ │
│ │ Query1 │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ >│
│ │ │
│ │ Record(s) │
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─│
│ │ │
│ Response1 │ │
│ <─ ─ ─ ─ ─ ─ ─ ─| │
┌──┴───┐ ┌──┴───┐ ┌────┴────┐
│Client│ │Server│ │Datastore│
└──────┘ └──────┘ └─────────┘
Customers got the best performance possible. Then the world changed, new requirements and designs came at startling pace. Then this happened... again and again...
┌──────┐ ┌──────┐ ┌─────────┐
│Client│ │Server│ │Datastore│
└──┬───┘ └──┬───┘ └────┬────┘
│ Request1 │ │
│ ─ ─ ─ ─ ─ ─ ─ ─>| │
│ │ │
│ │ Query1 │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>|
│ │ │
│ │ Record(s) for Query1│
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─|
│ │ │
│ Response1 │ │
│ <─ ─ ─ ─ ─ ─ ─ ─| │
│ │ │
│ Request2 │ │
│ ─ ─ ─ ─ ─ ─ ─ ─>| │
│ │ │
│ │ Query2 │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>|
│ │ │
│ │ Record(s) for Query2│
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │
│ Response2 │ │
│ <─ ─ ─ ─ ─ ─ ─ ─ │
│ │ │
│ Request3 │ │
│ ─ ─ ─ ─ ─ ─ ─ ─> │
│ │ │
│ │ Query3 │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
│ │ │
│ │ Record(s) for Query3│
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │
│ Response3 │ │
│ <─ ─ ─ ─ ─ ─ ─ ─ │
│ │ │
│ Request4 │ │
│ ─ ─ ─ ─ ─ ─ ─ ─> │
│ │ │
│ │ Query4 │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
│ │ │
│ │ Record(s) for Query4│
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │
│ Response4 │ │
│ <─ ─ ─ ─ ─ ─ ─ ─ │
┌──┴───┐ ┌──┴───┐ ┌────┴────┐
│Client│ │Server│ │Datastore│
└──────┘ └──────┘ └─────────┘
We always delivered changes, but the customers suffered due to the request waterfalls that cause sluggish experience, Server got more stressed. Datastore suffered with all these extra queries.
Then clients discovered GraphQL. My first introduction to GraphQL was as a great solution to reduce the API waterfall. Some libraries just made it super easy to glue together the backend APIs and abstract it behind a GraphQL schema. Clients rejoiced for it became this!
┌──────┐ ┌──────────────┐ ┌──────┐ ┌─────────┐
│Client│ │GraphQL_Server│ │Server│ │Datastore│
└──┬───┘ └──────┬───────┘ └──┬───┘ └────┬────┘
│ GraphQL Request │ │ │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─> │ │
│ │ │ │
│ │ Request1 │ │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─> │
│ │ │ │
│ │ │ Query1 │
│ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
│ │ │ │
│ │ │ Record(s) for Query1│
│ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │ │
│ │ Response1 │ │
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ │ │ │
│ │ Request2 │ │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─> │
│ │ │ │
│ │ │ Query2 │
│ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
│ │ │ │
│ │ │ Record(s) for Query2│
│ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │ │
│ │ Response2 │ │
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ │ │ │
│ │ Request3 │ │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─> │
│ │ │ │
│ │ │ Query3 │
│ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
│ │ │ │
│ │ │ Record(s) for Query3│
│ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │ │
│ │ Response3 │ │
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ │ │ │
│ │ Request4 │ │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─> │
│ │ │ │
│ │ │ Query4 │
│ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
│ │ │ │
│ │ │ Record(s) for Query4│
│ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │ │
│ │ Response4 │ │
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ │ │ │
│ GraphQL Response │ │ │
│ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │
┌──┴───┐ ┌──────┴───────┐ ┌──┴───┐ ┌────┴────┐
│Client│ │GraphQL_Server│ │Server│ │Datastore│
└──────┘ └──────────────┘ └──────┘ └─────────┘
The High speed network between GraphQL_Server and Server improved things quite a bit. Clients moved faster when changes came their way. Some Datastores made it cheaper to fetch all-the-things in one Query. Then I saw this strategy happen.
┌──────┐ ┌──────────────┐ ┌─────────┐
│Client│ │GraphQL_Server│ │Datastore│
└──┬───┘ └──────┬───────┘ └────┬────┘
│ GraphQL Request │ │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─> │
│ │ │
│ │─ ─ ┐
│ │ | Convert GraphQL Query to Datastore query
│ │< ─ ┘
│ │ │
│ │ Optimal Query1 │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
│ │ │
│ │ Record(s) for Query1 │
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │
│ GraphQL Response │ │
│ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
┌──┴───┐ ┌──────┴───────┐ ┌────┴────┐
│Client│ │GraphQL_Server│ │Datastore│
└──────┘ └──────────────┘ └─────────┘
This was very efficient when possible. But there are a few things that kept repeating every request.
- The client kept building the GraphQL query for every request.
- The server kept translating the GraphQL Query to Datastore Query every request.
Compiled GraphQL is just one more step ahead in this journey to avoid this two runtime overheads that are predictable at build time.
At build time, we persist the queries that get's repeated every request.
┌────────────┐ ┌───────────┐ ┌────────────┐
│Client_Build│ │Query_Store│ │Server_Build│
└─────┬──────┘ └─────┬─────┘ └─────┬──────┘
│────┐ │
│ │ Generate persisted │
│<───┘ Query PQ1 │
│ │
│ │ │
│ Persist Query PQ1 │ │
│ ──────────────────────> │
│ │ │
│ │ Get Persisted Queries│
│ │ <─────────────────────
│ │ │
│ │ │────┐
│ │ │ │ Generate Datastore query
│ │ │<───┘ DQ1 from PQ1
│ │ │
│ │ │
│ │ Persist Query │
│ │ DQ1 Mapped to PQ1 │
│ │ <─────────────────────
┌─────┴──────┐ ┌─────┴─────┐ ┌─────┴──────┐
│Client_Build│ │Query_Store│ │Server_Build│
└────────────┘ └───────────┘ └────────────┘
At runtime, thus we avoid the overhead.
┌──────┐ ┌──────────────┐ ┌─────────┐
│Client│ │GraphQL_Server│ │Datastore│
└──┬───┘ └──────┬───────┘ └────┬────┘
│ GraphQL Request with PQ1│ │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─> │
│ │ │
│ │─ ─ ┐
│ │ | Lookup corresponding Datastore query DQ1
│ │< ─ ┘
│ │ │
│ │ Datastore query with DQ1 │
│ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>
│ │ │
│ │ Record(s) │
│ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ │ │
│ GraphQL Response │ │
│ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
┌──┴───┐ ┌──────┴───────┐ ┌────┴────┐
│Client│ │GraphQL_Server│ │Datastore│
└──────┘ └──────────────┘ └─────────┘
If you look closely, this is the same old world we started in. The key difference is we are ever more powerful and flexible to adapt to the changing needs of the customers.
Summary
This concept can be realised using multiple implementations. I will be keen to learn what building blocks you will use to realise this? I will reserve my comments and biases for a follow up post where I will illustrate this with one or more concrete example(s).
Top comments (0)