Creating GraphQL clients in Go

When the Go program is a client for the GraphQL server, the client should understand how to compose GraphQL queries correctly and send them to the server. Go cannot do that natively, but it can with the help of an external package called machinebox/graphql. This is a lightweight client that allows developers to send queries and mutations to servers.

We can install the package using the dep tool:

dep ensure -add github.com/machinebox/graphql

Let's write a tool for fetching data from GitHub's GraphQL API. For that, create a project directory called graphqlClient:

mkdir -p $GOPATH/src/github.com/git-user/chapter10/graphqlClient
touch $GOPATH/src/github.com/git-user/chapter10/graphqlClient/main.go

The goal here is to fetch the details of all the available licenses for GitHub projects. GitHub provides an API to fetch all available licenses, but let's say we are only interested in the Apache2.0 license. So, we proceed with the GitHub GraphQL API to fetch the license resource. To make API calls to GitHub, you should add a bearer token in the headers, along with the request.

We worked with the GitHub API in Chapter 8Building a REST API Client in Go, to create a CLI client. There, we used a GITHUB_TOKEN, which acts as a personal access token or bearer token for API requests. An access token is a string that's passed to authenticate a user. We assume that you have the access token at hand (Chapter 8, Building a REST API Client in Go, to find out how and where to get it).

First, import the necessary packages. We need the graphql package and some other standard packages such as os to read the access token and log to print the response:

package main

import (
"context"
"log"
"os"

"github.com/machinebox/graphql"
)

We imported the graphql package as it is. If it is not available, run the following command:

dep init
dep ensure -add github.com/machinebox/graphql

The response for the GitHub license looks like this (from the documentation: https://developer.github.com/v4/object/license/):

{
"data": {
"license": {
"name": "string",
"description": "string"
}
}
}

There are many fields in the schema, but let's assume we are only interested in name and description. So, create a struct in our main program that holds this data structure:

// Response of API
type Response struct {
License struct {
Name string `json:"name"`
Description string `json:"description"`
} `json:"license"`
}

The graphql package provides a function called NewClient for creating a GraphQL client. It takes a GraphQL server endpoint as the only argument.

Once the client has been declared, we can create a new GraphQL client request using the graphql.NewRequest function. It takes a client query string as the argument:

func main() {
// create a client (safe to share across requests)
client := graphql.NewClient("https://api.github.com/graphql")

// make a request to GitHub API
req := graphql.NewRequest(`
query {
license(key: "apache-2.0") {
name
description
}
}
`)
// Next code goes here....
}

Once we have both the client and request objects, we can make queries. However, the GitHub API is secured and needs an access token for authorization. For that, we should add a header called Authorization with the 'bearer' token. The header can be calculated like this:

Authorization: 'bearer' + personal_access_token

We should concatenate the GitHub access token with the 'bearer ' string (note the space after "r") to form a bearer token. We should pass the whole string as a header to the GitHub GraphQL server. The code for this looks like this:

    var GithubToken = os.Getenv("GITHUB_TOKEN")
req.Header.Add("Authorization", "bearer "+GithubToken)

Here, we are reading a personal access token from the environment variable and putting it into the Authorization header. After this, we should create a context and actually fire the request to the server using the client.Run function. To do this, we can declare an instance of the Response struct and pass it to the Run function. When the query is successful, the JSON response is loaded into the struct instance so that we can access the result:

    // define a Context for the request
ctx := context.Background()

// run it and capture the response
var respData Response
if err := client.Run(ctx, req, &respData); err != nil {
log.Fatal(err)
}
log.Println(respData.License.Description)

Here, respData is the result struct that holds the response from the GraphQL server. Once we receive the response, we can log the description of the Apache2.0 license to the console.

Let's run the program and see the output:

go run github.com/git-user/chapter10/graphqlClient/main.go

This prints the license description to the console:

2019/12/15 23:16:25 A permissive license whose main conditions require preservation of copyright and license notices. Contributors provide an express grant of patent rights. Licensed works, modifications, and larger works may be distributed under different terms and without source code.

This is how a Go client can interact with a GraphQL server. Client queries can be changed but the procedure always remains the same.

In the next section, we will learn how to implement a server that is similar to the GitHub GraphQL server that is powering API V4. We'll take a simple example of a multiplayer game and try to define a schema API.