Next: , Previous: , Up: API   [Contents][Index]


3.3 Making GraphQL Requests

Function: ghub-graphql graphql &optional variables &key username auth host callback silent callback errorback value extra

This function makes a GraphQL request using GRAPHQL and VARIABLES as inputs. GRAPHQL is a GraphQL string. VARIABLES is a JSON-like alist. The other arguments behave as for ghub-request (which see).

The response is returned as a JSON-like alist. Even if the response contains errors, this function does not raise an error. Cursor-handling is likewise left to the caller.

ghub-graphql is a thin convenience wrapper around ghub-request, similar to ghub-post and friends. While the latter only hard-code the value of the METHOD argument, the former also hard-codes RESOURCE and constructs PAYLOAD from GRAPHQL and VARIABLES. It also drops UNPAGINATE, NOERROR, READER (internal functions expect alist-ified JSON) and FORGE (only Github currently supports GraphQL).

ghub-graphql does not account for the fact that pagination works differently in GraphQL than it does in REST, so users of this function have to deal with that themselves. Likewise error handling works differently and has to be done by the caller too.

An early attempt at implementing automatic unpaginating for GraphQL can be found in the faithful-graphql branch, provided I haven’t deleted that by now. On that branch I try to do things as intended by the designers of GraphQL, using variables and fragments, and drowning in a sea of boilerplate.

The problem with that approach is that it only works for applications that fetch specific information on demand and actually want things to be paginated. I am convinced that GraphQL is very nice for web apps.

However the Forge package for which I have implemented all of this has very different needs. It wants to fetch "all the data" and "cache" it locally, so that it is available even when there is no internet connection. GraphQL was designed around the idea that you should be able to "ask for what you need and get exactly that". But when that boils down to "look, if I persist, then you are going to hand me over all the data anyway, so just caught it up already", then things start to fall apart. If Github’s GraphQL allowed pagination to be turned off completely, then teaching ghub-graphql about error handling would be enough.

But it doesn’t and when doing things as intended, then that leads to huge amounts of repetitive boilerplate, which is so boring to write that doing it without introducing bugs left and right is near impossible; so I decided to give up on GraphQL variables, fragments and conditions, and instead implement something more powerful, though also more opinionated.

Function: ghub--graphql-vacuum query variables callback &optional until &key narrow username auth host forge

This function is an opinionated alternative to ghub-graphql. It relies on dark magic to get the job done.

It makes an initial request using QUERY. It then looks for paginated edges in the returned data and makes more requests to resolve them. In order to do so it automatically transforms the initial QUERY into another query suitable for that particular edge. The data retrieved by subsequent requests is then injected into the data of the original request before that is returned or passed to the callback. If subsequently retrieved data features new paginated edges, then those are followed recursively.

The end result is essentially the same as using ghub-graphql, if only it were possible to say "do not paginate anything". The implementation is much more complicated because it is not possible to do that.

QUERY is a GraphQL query expressed as an s-expression. The bundled gsexp library is used to turn that into a GraphQL query string. Only a subset of the GraphQL features are supported; fragments for example are not, and magical stuff happens to variables. This is not documented yet, I am afraid. Look at existing callers.

VARIABLES is a JSON-like alist as for ghub-graphql.

UNTIL is an alist ((EDGE-until . VALUE)...). When unpaginating EDGE try not to fetch beyond the element whose first field has the value VALUE and remove that element as well as all "lesser" elements from the retrieved data if necessary. Look at forge--pull-repository for an example. This is only useful if you "cache" the response locally and want to avoid fetching data again that you already have.

Other arguments behave as for ghub-graphql and ghub-request, more or less. If CALLBACK is nil, pretty-print the response.

Using ghub--graphql-vacuum, the following resource specific functions are implemented. These functions are not part of the public API yet and are very much subject to change.

Function: ghub-fetch-repository owner name callback &optional until &key username auth host forge

This function asynchronously fetches forge data about the specified repository. Once all data has been collected, CALLBACK is called with the data as the only argument.

Function: ghub-fetch-issue owner name callback &optional until &key username auth host forge

This function asynchronously fetches forge data about the specified issue. Once all data has been collected, CALLBACK is called with the data as the only argument.

Function: ghub-fetch-pullreq owner name callback &optional until &key username auth host forge

This function asynchronously fetches forge data about the specified pull-request. Once all data has been collected, CALLBACK is called with the data as the only argument.

Note that in order to avoid duplication all of these functions base their initial query on the query stored in ghub-fetch-repository. The latter two pass that query through ghub--graphql-prepare-query, which then uses ghub--graphql-narrow-query to remove parts the caller is not interested in. These two functions are also used internally, when unpaginating, but as demonstrated here they can be useful even before making an initial request.


Next: Github Convenience Wrappers, Previous: Making REST Requests, Up: API   [Contents][Index]