graphql

package module
v0.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2022 License: BSD-2-Clause Imports: 21 Imported by: 5

README

graphql-go Sourcegraph Build Status GoDoc

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 OpenTelemetry and OpenTracing standards
  • 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

Getting started

In order to run a simple GraphQL server locally create a main.go file with the following content:

package main

import (
        "log"
        "net/http"

        graphql "github.com/golangid/graphql-go"
        "github.com/golangid/graphql-go/relay"
)

type query struct{}

func (_ *query) Hello() string { return "Hello, world!" }

func main() {
        s := `
                type Query {
                        hello: String!
                }
        `
        schema := graphql.MustParseSchema(s, &query{})
        http.Handle("/query", &relay.Handler{Schema: schema})
        log.Fatal(http.ListenAndServe(":8080", nil))
}

Then run the file with go run main.go. To test:

curl -XPOST -d '{"query": "{ hello }"}' localhost:8080/query

For more realistic usecases check our examples section.

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 to noop.Tracer.
  • Logger(logger log.Logger) is used to log panics during query execution. It defaults to exec.DefaultLogger.
  • PanicHandler(panicHandler errors.PanicHandler) is used to transform panics into errors during query execution. It defaults to errors.DefaultPanicHandler.
  • DisableIntrospection() disables introspection queries.
Custom Errors

Errors returned by resolvers can include custom extensions by implementing the ResolverError interface:

type ResolverError interface {
	error
	Extensions() map[string]interface{}
}

Example of a simple custom error:

type droidNotFoundError struct {
	Code    string `json:"code"`
	Message string `json:"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
}
Tracing

By default the library uses noop.Tracer. If you want to change that you can use the OpenTelemetry or the OpenTracing implementations, respectively:

// OpenTelemetry tracer
package main

import (
	"github.com/golangid/graphql-go"
	"github.com/golangid/graphql-go/example/starwars"
	otelgraphql "github.com/golangid/graphql-go/trace/otel"
	"github.com/golangid/graphql-go/trace/tracer"
)
// ...
_, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(otelgraphql.DefaultTracer()))
// ...

Alternatively you can pass an existing trace.Tracer instance:

tr := otel.Tracer("example")
_, err = graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(&otelgraphql.Tracer{Tracer: tr}))
// OpenTracing tracer
package main

import (
	"github.com/golangid/graphql-go"
	"github.com/golangid/graphql-go/example/starwars"
	"github.com/golangid/graphql-go/trace/opentracing"
	"github.com/golangid/graphql-go/trace/tracer"
)
// ...
_, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(opentracing.Tracer{}))

// ...

If you need to implement a custom tracer the library would accept any tracer which implements the interface below:

type Tracer interface {
    TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*errors.QueryError))
    TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*errors.QueryError))
    TraceValidation(context.Context) func([]*errors.QueryError)
}
Examples

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ID

type ID string

ID represents GraphQL's "ID" scalar type. A custom type may be used instead.

func (ID) ImplementsGraphQLType

func (ID) ImplementsGraphQLType(name string) bool

func (ID) MarshalJSON

func (id ID) MarshalJSON() ([]byte, error)

func (*ID) UnmarshalGraphQL

func (id *ID) UnmarshalGraphQL(input interface{}) error

type NullBool added in v0.0.7

type NullBool struct {
	Value *bool
	Set   bool
}

NullBool is a string that can be null. Use it in input structs to differentiate a value explicitly set to null from an omitted value. When the value is defined (either null or a value) Set is true.

func (NullBool) ImplementsGraphQLType added in v0.0.7

func (NullBool) ImplementsGraphQLType(name string) bool

func (*NullBool) Nullable added in v0.0.7

func (s *NullBool) Nullable()

func (*NullBool) UnmarshalGraphQL added in v0.0.7

func (s *NullBool) UnmarshalGraphQL(input interface{}) error

type NullFloat added in v0.0.7

type NullFloat struct {
	Value *float64
	Set   bool
}

NullFloat is a string that can be null. Use it in input structs to differentiate a value explicitly set to null from an omitted value. When the value is defined (either null or a value) Set is true.

func (NullFloat) ImplementsGraphQLType added in v0.0.7

func (NullFloat) ImplementsGraphQLType(name string) bool

func (*NullFloat) Nullable added in v0.0.7

func (s *NullFloat) Nullable()

func (*NullFloat) UnmarshalGraphQL added in v0.0.7

func (s *NullFloat) UnmarshalGraphQL(input interface{}) error

type NullInt added in v0.0.7

type NullInt struct {
	Value *int32
	Set   bool
}

NullInt is a string that can be null. Use it in input structs to differentiate a value explicitly set to null from an omitted value. When the value is defined (either null or a value) Set is true.

func (NullInt) ImplementsGraphQLType added in v0.0.7

func (NullInt) ImplementsGraphQLType(name string) bool

func (*NullInt) Nullable added in v0.0.7

func (s *NullInt) Nullable()

func (*NullInt) UnmarshalGraphQL added in v0.0.7

func (s *NullInt) UnmarshalGraphQL(input interface{}) error

type NullString added in v0.0.7

type NullString struct {
	Value *string
	Set   bool
}

NullString is a string that can be null. Use it in input structs to differentiate a value explicitly set to null from an omitted value. When the value is defined (either null or a value) Set is true.

func (NullString) ImplementsGraphQLType added in v0.0.7

func (NullString) ImplementsGraphQLType(name string) bool

func (*NullString) Nullable added in v0.0.7

func (s *NullString) Nullable()

func (*NullString) UnmarshalGraphQL added in v0.0.7

func (s *NullString) UnmarshalGraphQL(input interface{}) error

type NullTime added in v0.0.7

type NullTime struct {
	Value *Time
	Set   bool
}

NullTime is a string that can be null. Use it in input structs to differentiate a value explicitly set to null from an omitted value. When the value is defined (either null or a value) Set is true.

func (NullTime) ImplementsGraphQLType added in v0.0.7

func (NullTime) ImplementsGraphQLType(name string) bool

func (*NullTime) Nullable added in v0.0.7

func (s *NullTime) Nullable()

func (*NullTime) UnmarshalGraphQL added in v0.0.7

func (s *NullTime) UnmarshalGraphQL(input interface{}) error

type Response

type Response struct {
	Errors     []*errors.QueryError   `json:"errors,omitempty"`
	Data       json.RawMessage        `json:"data,omitempty"`
	Extensions map[string]interface{} `json:"extensions,omitempty"`
}

Response represents a typical response of a GraphQL server. It may be encoded to JSON directly or it may be further processed to a custom response type, for example to include custom error data. Errors are intentionally serialized first based on the advice in https://github.com/facebook/graphql/commit/7b40390d48680b15cb93e02d46ac5eb249689876#diff-757cea6edf0288677a9eea4cfc801d87R107

type Schema

type Schema struct {
	// contains filtered or unexported fields
}

Schema represents a GraphQL schema with an optional resolver.

func MustParseSchema

func MustParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) *Schema

MustParseSchema calls ParseSchema and panics on error.

func ParseSchema

func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (*Schema, error)

ParseSchema parses a GraphQL schema and attaches the given root resolver. It returns an error if the Go type signature of the resolvers does not match the schema. If nil is passed as the resolver, then the schema can not be executed, but it may be inspected (e.g. with ToJSON).

func (*Schema) ASTSchema added in v0.0.8

func (s *Schema) ASTSchema() *types.Schema

func (*Schema) Exec

func (s *Schema) Exec(ctx context.Context, queryString string, operationName string, variables map[string]interface{}) *Response

Exec executes the given query with the schema's resolver. It panics if the schema was created without a resolver. If the context get cancelled, no further resolvers will be called and a the context error will be returned as soon as possible (not immediately).

func (*Schema) Inspect

func (s *Schema) Inspect() *introspection.Schema

Inspect allows inspection of the given schema.

func (*Schema) Subscribe

func (s *Schema) Subscribe(ctx context.Context, queryString string, operationName string, variables map[string]interface{}) (<-chan interface{}, error)

Subscribe returns a response channel for the given subscription with the schema's resolver. It returns an error if the schema was created without a resolver. If the context gets cancelled, the response channel will be closed and no further resolvers will be called. The context error will be returned as soon as possible (not immediately).

func (*Schema) ToJSON

func (s *Schema) ToJSON() ([]byte, error)

ToJSON encodes the schema in a JSON format used by tools like Relay.

func (*Schema) Validate

func (s *Schema) Validate(queryString string) []*errors.QueryError

Validate validates the given query with the schema.

func (*Schema) ValidateWithVariables added in v0.0.7

func (s *Schema) ValidateWithVariables(queryString string, variables map[string]interface{}) []*errors.QueryError

ValidateWithVariables validates the given query with the schema and the input variables.

type SchemaOpt

type SchemaOpt func(*Schema)

SchemaOpt is an option to pass to ParseSchema or MustParseSchema.

func DirectiveFuncs added in v0.0.9

func DirectiveFuncs(dirFuncs map[string]types.DirectiveFunc) SchemaOpt

DirectiveFuncs allows to pass custom directive visitors that will be able to handle your GraphQL schema directives.

func DisableIntrospection

func DisableIntrospection() SchemaOpt

DisableIntrospection disables introspection queries.

func Logger

func Logger(logger log.Logger) SchemaOpt

Logger is used to log panics during query execution. It defaults to exec.DefaultLogger.

func MaxDepth

func MaxDepth(n int) SchemaOpt

MaxDepth specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking.

func MaxParallelism

func MaxParallelism(n int) SchemaOpt

MaxParallelism specifies the maximum number of resolvers per request allowed to run in parallel. The default is 10.

func PanicHandler added in v0.0.8

func PanicHandler(panicHandler errors.PanicHandler) SchemaOpt

PanicHandler is used to customize the panic errors during query execution. It defaults to errors.DefaultPanicHandler.

func SubscribeResolverTimeout added in v0.0.7

func SubscribeResolverTimeout(timeout time.Duration) SchemaOpt

SubscribeResolverTimeout is an option to control the amount of time we allow for a single subscribe message resolver to complete it's job before it times out and returns an error to the subscriber.

func Tracer

func Tracer(t tracer.Tracer) SchemaOpt

Tracer is used to trace queries and fields. It defaults to tracer.Noop.

func UseFieldResolvers

func UseFieldResolvers() SchemaOpt

UseFieldResolvers specifies whether to use struct field resolvers

func UseStringDescriptions

func UseStringDescriptions() SchemaOpt

UseStringDescriptions enables the usage of double quoted and triple quoted strings as descriptions as per the June 2018 spec https://facebook.github.io/graphql/June2018/. When this is not enabled, comments are parsed as descriptions instead.

func ValidationTracer

func ValidationTracer(tracer tracer.LegacyValidationTracer) SchemaOpt

ValidationTracer is used to trace validation errors. It defaults to tracer.LegacyNoopValidationTracer. Deprecated: context is needed to support tracing correctly. Use a Tracer which implements tracer.ValidationTracer.

type Time

type Time struct {
	time.Time
}

Time is a custom GraphQL type to represent an instant in time. It has to be added to a schema via "scalar Time" since it is not a predeclared GraphQL type like "ID".

func (Time) ImplementsGraphQLType

func (Time) ImplementsGraphQLType(name string) bool

ImplementsGraphQLType maps this custom Go type to the graphql scalar type in the schema.

func (Time) MarshalJSON

func (t Time) MarshalJSON() ([]byte, error)

MarshalJSON is a custom marshaler for Time

This function will be called whenever you query for fields that use the Time type

func (*Time) UnmarshalGraphQL

func (t *Time) UnmarshalGraphQL(input interface{}) error

UnmarshalGraphQL is a custom unmarshaler for Time

This function will be called whenever you use the time scalar as an input

Directories

Path Synopsis
example
caching/cache
Package cache implements caching of GraphQL requests by allowing resolvers to provide hints about their cacheability, which can be used by the transport handlers (e.g.
Package cache implements caching of GraphQL requests by allowing resolvers to provide hints about their cacheability, which can be used by the transport handlers (e.g.
starwars
Package starwars provides a example schema and resolver based on Star Wars characters.
Package starwars provides a example schema and resolver based on Star Wars characters.
internal
The trace package provides tracing functionality.
The trace package provides tracing functionality.
noop
Package noop defines a no-op tracer implementation.
Package noop defines a no-op tracer implementation.
Package types represents all types from the GraphQL specification in code.
Package types represents all types from the GraphQL specification in code.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL