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
schema
option 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.go
Let’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: generated
Now 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.GetRepository
function, with typed inputs. Here’s what I see when I hover over it. - A neatly defined
generated.GetRepositoryRepository
type. 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.yaml
But 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.yaml
Conclusion
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.