GraphQL in Smalltalk
October 06, 2022
These days most of my analysis - or even short scripts! - start in GToolkit. GToolkit’s paradigm of executable notebooks - with human viewable results - means I can write a script, do my business logic, and not focus on display formatting (much).
Awesome!
As part of my weekly bujo review I want to list all the PRs I’ve closed, on Github, in the last week or so. Now, with the Github GraphQL API I can put that query together with a dozen lines of declarative GraphQL
query {
viewer {
pullRequests(first: 24, states: MERGED, orderBy: {field: UPDATED_AT, direction: DESC}) {
nodes {
repository {
nameWithOwner
}
closedAt
url
title
}
}
}
}
For my user, gives me all my pull requests that are merged, and for each node give me repository name, when I closed it, URL and title.
Super coool. I could run it in Postman… but if I ran it from GToolkit I’d get a bunch of stuff for free, as mentioned. Except there’s no Smalltalk library for GraphQL. So I wrote my own, the graphqlclient-smalltalk.
It’s a very simple GraphQL client for Smalltalk, and missing a lot of things you’ll find in real GraphQL clients. (If you need these things, pull requests welcome…)
Let’s see it in action (both the code and the resulting objects!)
The GraphQL client has the following important methods:
graphqlEndpoint:
<— URL to send all this stuff tooperationBody:
<— what is your GraphQL query/mutationoperationVariables:
<— GraphQL variablesexecuteQuery
<— do it
Now, how do I get my data out?
executeQuery
returns an OperationResult result object, with two helper functions: data
and errors
.
You could use this object directly, like so
| q res output |
q := GraphQLClient new.
q znclient setBearerAuthentication: KindaSecret githubAuth.
q graphqlEndpoint: 'https://api.github.com/graphql'.
q operationBody: 'query {
viewer {
login
}
}
'.
res := q executeQuery.
res data.
Maybe your GraphQL result is very complicated, and you want to put some clever object traversal or abstraction around it? Great! Create a subclass of OperationResult
and define helper methods!
| q res customOutput |
"... not going to recopy all that boilerplate..."
res := q executeQuery.
customOutput := MyOperationResultSubclassIDefined newFromOperationResult: res.
customOutput displayMyName. "<-- a custom method I declared! Look Ma, abstraction!"
OperationResult
defines a few helpers for working with query results, and also defines a standard factory function method for subclasses, newFromOperationResult
. No matter what the subclass, it can be constructed using this method!
Conclusion
During the creation of this module I really came to appreciate the flexibility of the built-in Smalltalk string literal. Multiline strings just worked right out of the box, I didn’t have to do anything weird. Smalltalk quickly let me iterate on this basic chunk of work, and get something very basic working quickly!
Additionally, during the initial task - get me all that data from Github - I really appreciated the power of GraphQL. In a REST API that would have been multiple query calls manually stiched together - something I experienced just after this when I sarted playing with the Github notifications REST api.