Verb is a capable mode for Emacs and Org mode that allows to define and perform HTTP calls. Besides offering many useful features on its own, it also provides multiple ways how to extends its capabilities. Thanks to them, it can be easily turned into GraphQL client.

Key to this is property Verb-Map-Request. This property can hold a function that modifies HTTP request. The function receives object of verb-request-spec class and returns this object modified.

In order to turn a bare GraphQL query into HTTP request, it has to be put, as a string, into JSON object under the key query. Variables for the query can be attached to this object under key variables. This can be achieved by this request-mapping function:

(defun verb-graphql (rs)
  "Converts Verb-mode's request object RS into a GraphQL request.
For use with Verb-Map-Request property."
  (pcase-let* ((`(,query ,variables) (split-string (oref rs body) ":variables:"))
               (body `(:query ,query :variables ,(json-parse-string variables))))
    (oset rs body (json-encode body)))
  rs)

In the following Org file, Verb-Map-Request is set to verb-graphql. The string :variables: serves as a separator of the main request and variables. Perhaps there are better ways how to pass variables to the mapping function. org-use-property-inheritance has to be set to t so that the Verb-Map-Request property is visible in whole subtree.

C-c C-c in the source block will make the GraphQL call. Any other Verb functions, e. g. verb-export-request-on-point-curl still work as expected.

* Github GraphQL API                                                   :verb:
:properties:
:Verb-Map-Request: verb-graphql
:end:

TEMPLATE https://api.github.com/graphql
User-Agent: emacs-test
Authorization: Bearer {{(shell-command-to-string "gh auth token")}}

** Get Pull Requests

POST

#+begin_src graphql
  query GetPullRequests($owner: String!, $name: String!) {
    repository(owner: $owner, name: $name) {
      pullRequests(first: 10, after: null) {
        nodes {
          createdAt
          number
          title
        }
        pageInfo {
          endCursor
          startCursor
          hasNextPage
          hasPreviousPage
        }
      }
    }
  }

  :variables:

  {
    "owner": "rust-bitcoin",
    "name": "rust-bitcoin"
  }
#+end_src

* Variables

# Local Variables:
# org-use-property-inheritance: t
# eval: (verb-mode)
# End:

References Link to heading

Heavily inspired by u/rontan who shared this idea in a Reddit comment. Thanks!