5.7 KiB
graphql-go
The goal of this project is to provide full support of the GraphQL draft specification with a set of idiomatic, easy to use Go packages.
While still under heavy development (internal
APIs are
almost certainly subject to change), this library is safe for production
use.
Features
- minimal API
- support for
context.Context
- support for the
OpenTracing
standard - schema type-checking against resolvers
- resolvers are matched to the schema based on method sets (can resolve a GraphQL schema with a Go interface or Go struct).
- handles panics in resolvers
- parallel execution of resolvers
- subscriptions
Roadmap
We’re trying out the GitHub Project feature to manage
graphql-go
’s development
roadmap. Feedback is welcome and appreciated.
(Some) Documentation
Basic Sample
package main
import (
"log"
"net/http"
"github.com/graph-gophers/graphql-go"
graphql "github.com/graph-gophers/graphql-go/relay"
)
type query struct{}
func (_ *query) Hello() string { return "Hello, world!" }
func main() {
:= `
s type Query {
hello: String!
}
`
:= graphql.MustParseSchema(s, &query{})
schema .Handle("/query", &relay.Handler{Schema: schema})
http.Fatal(http.ListenAndServe(":8080", nil))
log}
To test:
curl -XPOST -d '{"query": "{ hello }"}' localhost:8080/query
Resolvers
A resolver must have one method or field for each field of the
GraphQL type it resolves. The method or field name has to be exported and
match the schema’s field’s name in a non-case-sensitive way. You can use
struct fields as resolvers by using
SchemaOpt: UseFieldResolvers()
. For example,
opts := []graphql.SchemaOpt{graphql.UseFieldResolvers()}
schema := graphql.MustParseSchema(s, &query{}, opts...)
When using UseFieldResolvers
schema option, a struct
field will be used only when: - there is no method for a struct
field - a struct field does not implement an interface method - a struct
field does not have arguments
The method has up to two arguments:
- Optional
context.Context
argument. - Mandatory
*struct { ... }
argument if the corresponding GraphQL field has arguments. The names of the struct fields have to be exported and have to match the names of the GraphQL arguments in a non-case-sensitive way.
The method has up to two results:
- The GraphQL field’s value as determined by the resolver.
- Optional
error
result.
Example for a simple resolver method:
func (r *helloWorldResolver) Hello() string {
return "Hello world!"
}
The following signature is also allowed:
func (r *helloWorldResolver) Hello(ctx context.Context) (string, error) {
return "Hello world!", nil
}
Schema Options
UseStringDescriptions()
enables the usage of double quoted and triple quoted. When this is not enabled, comments are parsed as descriptions instead.UseFieldResolvers()
specifies whether to use struct field resolvers.MaxDepth(n int)
specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking.MaxParallelism(n int)
specifies the maximum number of resolvers per request allowed to run in parallel. The default is 10.Tracer(tracer trace.Tracer)
is used to trace queries and fields. It defaults totrace.OpenTracingTracer
.ValidationTracer(tracer trace.ValidationTracer)
is used to trace validation errors. It defaults totrace.NoopValidationTracer
.Logger(logger log.Logger)
is used to log panics during query execution. It defaults toexec.DefaultLogger
.PanicHandler(panicHandler errors.PanicHandler)
is used to transform panics into errors during query execution. It defaults toerrors.DefaultPanicHandler
.DisableIntrospection()
disables introspection queries.
Custom Errors
Errors returned by resolvers can include custom extensions by
implementing the ResolverError
interface:
type ResolverError interface {
error
() map[string]interface{}
Extensions}
Example of a simple custom error:
type droidNotFoundError struct {
string `json:"code"`
Code string `json:"message"`
Message }
func (e droidNotFoundError) Error() string {
return fmt.Sprintf("error [%s]: %s", e.Code, e.Message)
}
func (e droidNotFoundError) Extensions() map[string]interface{} {
return map[string]interface{}{
"code": e.Code,
"message": e.Message,
}
}
Which could produce a GraphQL error such as:
{
"errors": [
{
"message": "error [NotFound]: This is not the droid you are looking for",
"path": [
"droid"
],
"extensions": {
"code": "NotFound",
"message": "This is not the droid you are looking for"
}
}
],
"data": null
}