Building a minimal GraphQL service in Go
Categories: programming
I figured it’s time to get serious about learning Go. Primarily since there has been a major market shift in the past few years as well as some interesting design ramifications. Plus it will help me understand parts of Kubernetes a bit better when I read the source.
I was hoping to build a GraphQL service. There are many implementations of GraphQL bindings for Go.
- github.com/graphql-go/graphql looks like a rather bare-bones implemenation.
- graph-gophers/graphql-go looks promising since it supports contexts and OpenTracing out of the box, though the resolver system looks a bit strange.
- 99designs/gqlgen has a codegen component which I am not entirely comfortable with at this point of my go adventures.
- graphql-go/relay appears to be really rudimentary.
- machinebox/graphql is a client.
- samsarahq/thunder looks promising. Especially with the parallelism. In the future this might be one I experiment with.
- appointy/jaal: This seems like an implemenation however I could not gather any real differentiating factors. Perahps in the future I will have a better understanding of the competitive advantage of this library.
After a brief 15 minute look at these, I think I’ll be going with graph-gophers/graphql-go for right now. Time to setup a simple project. GoLand has definitely improved since the last time I gave it a whirrl.
I dropped the example into a file title palatable.go
and ran the following wiht go1.14
on teh path:
go mod init git.meschbach.com/mee/palatable
go build
./palatable
Once I resolved the port conflict for tcp/8080
the service started fine. When I navigated to /query
I received a
simple response entity EOF
. When running the following example query it worked as expected:
$ curl -XPOST -d '{"query": "{ hello }"}' localhost:8080/query
stdout>{"data":{"hello":"Hello, world!"}}
Now time to actually get something done with the system. I decided to extend the system to provide some user metadata since most of the systems I build for personal use eventually have this. In this case just simply asking if the user is authenticated via the following schema:
type Query {
hello: String!
user: UserQuery
}
type UserQuery {
isAuthenticated: Boolean
}
Easy enough to implement via the following setup:
type UserQuery struct {
}
func (q *UserQuery) IsAuthenticated() bool {
return true;
}
type query struct{}
func (_ *query) Hello() string { return "Hello, world!" }
func (_ *query) User() *UserQuery {
return &UserQuery{};
}
Or so I thought! When attempting to run the application I recieved the following:
$ ./palatable
stderr>panic: bool is not a pointer
stderr> used by (*main.UserQuery).IsAuthenticated
stderr> used by (*main.query).User
stderr>
stderr>goroutine 1 [running]:
stderr>github.com/graph-gophers/graphql-go.MustParseSchema(...)
stderr> /Users/meschbach/.go/pkg/mod/github.com/graph-gophers/graphql-go@v0.0.0-20200309224638-dae41bde9ef9/graphql.go:57
stderr>main.main()
stderr> /Users/meschbach/wc/mee/palatable/palatable.go:37 +0x18c
Turns out you can’t just take the address of a boolean (return &true
) either. I found an example of how to convert a
boolean to via a Pet Shope example’s helpers
which is a bit odd. Perhaps this was the talking point about scalar support in the previous systems?
``golang func boolP(b bool) *bool { return &b }
func (q *UserQuery) IsAuthenticated() *bool { return boolP(true); } ``
Well that is a good start. Next time block I work on it I will be able to begin implementing actual domain logic.