## Summary:
One common use of fragment spreads is as the entirety of a field's
selection, e.g.
```graphql
query MyQuery {
myField {
...MyFragment
}
}
```
In this case, by default, genqlient generates a wrapper type
`MyQueryMyFieldMyType`, which just embeds `MyFragment`. This makes
sense if you later want to add more fields in addition to the fragment
spread. But if you don't -- and you did the fragment because you want
to share types, it's an extra layer of indirection. (Which becomes
especially onerous if `myField` has list type (`[MyType!]`), such that
it's not just an extra attribute-access to get to `MyFragment`.)
The new option `# @genqlient(flatten: true)` simplifies this situation:
if applied to `myField` is skips the wrapper type;
`MyQueryResponse.MyField` will simply have type `MyFragment` (or
`[]MyFragment`, or whatever). This should hopefully make the `typename`
option, which has more limitations, less necessary.
Note that in #30 the initial idea was to support this for fields as
well. This would require significant additional complexity in the
JSON-(un)marshaling code, and has proven less necessary, so I
implemented this option only for fragment-spreads for now. With that
restriction, it was shockingly simple; we have to hook into a bunch of
different places, but they're all quite simple, since the structure of
the Go types still matches the structure in GraphQL.
Issue: https://github.com/Khan/genqlient/issues/30
## Test plan:
make check
Author: benjaminjkraft
Reviewers: csilvers, dnerdy, aberkan, jvoll, mahtabsabet, MiguelCastillo, StevenACoffman
Required Reviewers:
Approved By: csilvers, dnerdy
Checks: ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint, ✅ Test (1.17), ✅ Test (1.16), ✅ Test (1.15), ✅ Test (1.14), ✅ Lint
Pull Request URL: https://github.com/Khan/genqlient/pull/121
genqlient: a truly type-safe Go GraphQL client
What is genqlient?
genqlient is a Go library to easily generate type-safe code to query a GraphQL API. It takes advantage of the fact that both GraphQL and Go are typed languages to ensure at compile-time that your code is making a valid GraphQL query and using the result correctly, all with a minimum of boilerplate.
genqlient provides:
- Compile-time validation of GraphQL queries: never ship an invalid GraphQL query again!
- Type-safe response objects: genqlient generates the right type for each query, so you know the response will unmarshal correctly and never need to use
interface{}. - Production-readiness: genqlient is used in production at Khan Academy, where it supports millions of learners and teachers around the world.
How do I use genqlient?
You can download and run genqlient the usual way: go run github.com/Khan/genqlient. To set your project up to use genqlient, see the getting started guide, or the example. For more complete documentation, see the docs.
How can I help?
genqlient welcomes contributions! Check out the (Contribution Guidelines), or file an issue on GitHub.
Why another GraphQL client?
Most common Go GraphQL clients have you write code something like this:
query := `query GetUser($id: ID!) { user(id: $id) { name } }`
variables := map[string]interface{}{"id": "123"}
var resp struct {
Me struct {
Name graphql.String
}
}
client.Query(ctx, query, &resp, variables)
fmt.Println(query.Me.Name)
// Output: Luke Skywalker
This code works, but it has a few problems:
- While the response struct is type-safe at the Go level; there's nothing to check that the schema looks like you expect. Maybe the field is called
fullName, notname; or maybe you capitalized it wrong (since Go and GraphQL have different conventions); you won't know until runtime. - The GraphQL variables aren't type-safe at all; you could have passed
{"id": true}and again you won't know until runtime! - You have to write everything twice, or hide the query in complicated struct tags, or give up what type safety you do have and resort to
interface{}.
These problems aren't a big deal in a small application, but for serious production-grade tools they're not ideal. And they should be entirely avoidable: GraphQL and Go are both typed languages; and GraphQL servers expose their schema in a standard, machine-readable format. We should be able to simply write a query and have that automatically validated against the schema and turned into a Go struct which we can use in our code. In fact, there's already good prior art to do this sort of thing: 99designs/gqlgen is a popular server library that generates types, and Apollo has a codegen tool to generate similar client-types for several other languages. (See docs/DESIGN.md for more prior art.)
genqlient fills that gap: you just specify the query, and it generates type-safe helpers, validated against the schema, that make the query.