Of course all this can be done by just wrapping these services in a some methods to do the prep work, but using a proxy seems a nicer solution, despite being a little more complex to get your head around at first. This is just a proof of concept, but I like where it might lead.
With an enhanced UrlFetch, I just want to just call fetch with the GraphQL query as an argument. This means the fetch has also to do these things
- Prepare the auth, other headers and standard endpoint
- Deal with error handling, parsing and structuring of the result
- Use and maintain cache to avoid unnecessary calls
The UrFetch proxy will also make calls to the cache service, so I want to enhance that to
- Stringify and parse results
- Maintain cache freshness data
- Compress content
- Build keys from query content
What is Proxy
Proof of concept
For simplicity, I’m going to redo the bmSwopCx library – see Currency exchange rates library for Apps Script – optimized for the parsimonious – swop.cx for the original implementation details.
My final class will look like this, with only 4 methods and a constructor, plus some static stuff and all the the required gql queries and fragments.
As you work through the constructor preparation, it will seem a little complex, but the key thing here is that the body of the class will be clean and simple, and the constructor should be re-usable for any GraphQL API by just modifying the endpoint and Auth.
The fetcher received as an argument by the constructur is the UrlFetchApp.fetch method. That’s what we’re going to create a proxy for, but first we need to do a bit of prep work.
There’s no need to keep recreating and passing the headers and endpoint to the fetcher, as in this API and GraphQL in general, they’ll be pretty standard, so let’s set them up just once
The fetcher is also going to work with cache, so before we set up the fetch proxt, let’s go ahead and set up the proxies we’ll need for the cacheservice. Before we do that though, we’ll need a couple of static utility functions to generate keys from content, and to compress and uncompress.
Create a digest to use a key
We’ll use the body of the query and url and a prefix associated with this class. This will make sure its unique and reproducable. A digest will look something like this.
and will be used as a key to results produced by the query from which it was generated
Zip and Unzip
I generally use lzString for compression so it’s compatible across platforms (Node and Apps Script), but since this class is only intended for Apps Script, we may as well use the zip Utilities to avoid bringing in extra libraries. The result is also encoded/decoded into/from base64 string so that the cacheservice can handle it as a regular string
Cache apply handlers
We’ll need 2 handlers for the cacheservice proxy, as we need to intercept the handling of .get and .put to do some extra stuff.
This generates a key from the request content, makes a regular cache.get call, then unzips and parses the result if there is one. Note that the arguments it receives are different to the ones regular cache.get would receive. Since it has responsibilty of generating a key, it needs the request content to make it from.
Similar to the getHandler, this also needs different arguments to the regular cache.set method so it can make a key from the request content. This adds some timestamp data, stringifies and compresses the data before calling cache.set to write it out.
We’re almost ready to create the cache proxy, but first we need to create a handler for that proxy. It needs to intercept calls to cache.get and set to return proxies using the cacheGet and cacheSet handlers
Now we can create the cache proxy. Any accesses of the get or set property will be intercepted by cacheGet and cacheSet handlers and instead of returning the cache.get or cache.set methods for execution it will return our modified versions of them as a proxy.
Now we can get back to creating an apply handler for the UrlFetchApp.fetch method. This will execute in place of the regular fetch method. This orchestrates the enhanced fetch workflow, including caching, error handling and response structuring
Finally, create the fetcher proxy using the handler above
Now we’re good to go, and can create very simple methods that invisibly use all that stuff we’ve just set up using the fetcher proxy.
Full class code
Although not especially relevant to the subject, I’ve included the specific GQL query data for this particular API. It may be useful if you are using this method to create your own GraphQL API handler.
This library is interchangable with the original Currency exchange rates library for Apps Scrip-optimized for the parsimonious -swop.cx and won’t offer any execution benefits over it, except that the code in this one is now a reusable model for any other GQL API library access.
bmSwopProxy : IDE
bmSwopProxy library: 18ncSp3–MSV1_svV4mnEPJGNepoe06pZfWR1gT1Mi0G9dVrE8a8PYlPw
bmSwopProxy github: https://github.com/brucemcpherson/bmSwopCxProxy
- A handier way of accessing Google Drive folders and files from Apps Script
- Apps Script V8: Arraybuffers and Typed arrays
- Apps Script V8: Arraybuffers and Typed arrays, endianness and views
- Apps Script V8: Keystore for global space
- Apps Script V8: Maps and Sets
- Apps Script V8: Multiple script files, classes & namespaces
- Apps Script V8: Provoke server side code from add-ons and htmlservice
- Apps Script V8: Sorting out the call stack to figure out who called
- Apps Script V8: spreading and destructuring
- Apps Script V8: Template literals
- ES6 Symbols – what on earth is all that about ?
- Proxy implementation of Apps Script GraphQL Class