At the very beginning, let’s review how we utilize GraphQL over HTTP(s) protocol in its essential:
- We have a
- It receives 2 parameters:
- It can be revoked by
So, without any overhead, we could just send plain HTTP(s) request via
axios library or even
fetch as usual (no need to introduce Apollo — the big monster):
As we can see clearly, it is pretty straightforward to write a factory class to generate an
axios instance with the necessary data carried every time we want to make a request.
Then it is the time to play with
Persisted GraphQL Query. But what it is?
In a nutshell: turn your
query into an
id and send it with
variables to the
/graphql endpoint, so no
query parameter would be passed to the backend.
You might be curious about why we should do this. Well, currently I can come up with several reasons but I am sure the list should be expended:
- If you want to whitelist your api calls with GraphQL (prevent your GraphQL api from being invoked by malicious users: some queries may increase your database pressure or be harmful in security aspect), you may want to identify queries by
id, not full
- If you want to save the bandwidth when call the GraphQL api: an
idshould always be much shorter than a full
querytext, isn’t it?
- If you want to cache the result based on
query, then an
idshould be perfect for part of the cache key (perhaps use with variables).
So far, we got the basic idea of
Persisted GraphQL Query, then let’s have look at how to implement it in our application.
Basically, there are 4 steps we need to do (assume that we have already put our query text in
- Collect our
*.graphqlfiles and generate a
- key: compressed query text
- value: id (uuid is the preferred format)
- Before sending actual request to
/graphqlendpoint, do some pre-process in the client side: remove
- Copy the same
mapping.jsonfile to backend or somewhere the backend can access
- Before dealing with the actual
/graphqlendpoint, do some pre-process in the server side: load
mapping.jsonfile, flip the array (now, key is
id, value is compressed
query), and find the actual
querytext according to the
idin the request parameters.
Let’s go through a step-by-step process for more details.
As Step 0, you should already organized your
*.graphql files like below:
Notice: it is not necessary to put everything in the same level. That is, feel free to organize those files in a hierarchical folder structure if you wish.
Step 1: use this open-source library to extract queries from those
*.graphql files and generate the mapping file. The instructions are listed below:
npm install -g graphql-extractor
graphql-extract ./graphql/ ./graphql/mapping.json
Step 2: add the pre-process code to the
A little trick here is that we compress the query been passed into the factory method to maintain the consistent with the mapping file (queries in the mapping file are compressed too). So in this case, once you pass a query that already exists in the mapping file, axios will use
id to do the request instead of
query text (the other parts of your code keep unchanged).
Step 3: This step depends on your preference or need. Currently, you can embed it in YAML config file of SilverStripe, include it in your source code files, or store it in somewhere accessible from your SilverStripe backend via HTTP(s).
Below is an example to embed the mapping file in YAML config file:
Based on your need, there are 3 provider class ready to use:
FileProvider. You may also roll your own and inject it as the
PersistedQueryMappingProvider if needed.
Step 4: This step is actually achieved by
silverstripe/silverstripe-graphql module, and you can just rely on it. Unfortunately, the pull request to the official repository has not been merged yet, it is welcome to use my fork of this repo at this moment. To do this, you need to modify your
Finally, let’s have a glance of the result:
One more thing, if you want to find an open-source example that actually use this feature, feel free to checkout https://github.com/zzdjk6/Groot. It is one of my side projects, and it is intended to be a self-hosted music platform (roll your own Spotify, yeah :)