Genqlient
At work, we do something useful with GraphQL & TypeScript — we generate types, so that a client can make typed calls to the API.
It’s a good development experience, and one of the strengths of GraphQL. I’ve been on the lookout for a similar experience in Go when working with a GraphQL API.
I recently came across Khan Academy’s genqlient, so I wanted to evaluate it.
Example
We’ll be using the Github GraphQL API.
In a simple Go project, I’ve written my GraphQL query in a file called repository.graphql.
query GetRepository($name: String!, $owner: String!) { repository(name: $name, owner: $owner) { name description url stargazerCount }}Here’s a few configuration things I’ve done:
- We have a configuration file,
genqlient.yaml, where we’re defining configuration options for Genqlient. - Download the GraphQL schema file. You can download it here. The filename needs to match the
schemaoption in our Genqlient configuration file. I’ll call itgithub.graphql.
Here is the layout of my project:
├── generated│ └── graphql.go├── genqlient.yaml├── github.graphql├── go.mod├── go.sum├── graphql│ └── repository.graphql└── main.goLet’s review the configuration file so far.
# the schema for our APIschema: github.graphql
# Our GraphQL filesoperations: - "graphql/*.graphql"
# where the generated code will livegenerated: generated/graphql.gopackage: generatedNow I’ll use that query:
1package main2
3import (4 "context"5 "fmt"6 "net/http"7
8 "github.com/Khan/genqlient/graphql"9 "github.com/alexhwoods/evaluate-genqlient/generated"10)11
12//go:generate go run github.com/Khan/genqlient genqlient.yaml13
14const GithubGraphqlAPI = "https://api.github.com/graphql"15
16
17func main() {18 key := "<personal-access-token>"19
20 ctx := context.Background()21
22 // I'm glossing over authentication - see full example23 client := graphql.NewClient(GithubGraphqlAPI, &httpClient)24 resp, err := generated.GetRepository(ctx, client, "kubernetes", "kubernetes")25
26 if err != nil {27 fmt.Printf("error: %s", err)28 }29
30 useRepository(resp.Repository)31}32
33func useRepository(repository generated.GetRepositoryRepository) {34 fmt.Printf("repository.Name: %v\n", repository.Name)35 fmt.Printf("repository.Description: %v\n", repository.Description)36 fmt.Printf("repository.StargazerCount: %v\n", repository.StargazerCount)37 fmt.Printf("repository.Url: %v\n", repository.Url)38}We have two useful things from the generated code:
- A
generated.GetRepositoryfunction, with typed inputs. Here’s what I see when I hover over it.
- A neatly defined
generated.GetRepositoryRepositorytype. If you dislike the naming there, note that it’s<query-name><type-name>, which I think is the right decision.
Using go generate
The command that we want to run to generate code is
go run github.com/Khan/genqlient genqlient.yamlBut that’s a handful. Luckily go has the go generate command, which scans our Go files for compiler directives. We will add one to our main.go file:
//go:generate go run github.com/Khan/genqlient genqlient.yamlConclusion
While there are a few details I’m leaving out (see full example), this library does exactly what it says it does.
When I did come across problems, the error messages were helpful, and I wasn’t stuck for long. I would have no problem using it in production.
Wow! You read the whole thing. People who make it this far sometimes
want to receive emails when I post something new.
I also have an RSS feed.