graphql

package
v0.7.1 Latest Latest
Warning

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

Go to latest
Published: Feb 17, 2020 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package graphql provides a GraphQL execution engine. During execution, a GraphQL server transforms requests into Go method calls and struct field accesses. This package follows the specification laid out at https://graphql.github.io/graphql-spec/June2018/

For the common case where you are serving GraphQL over HTTP, see the graphqlhttp package in this module.

Field Resolution

When executing a request, the server will first check the object to see whether it implements FieldResolver. If so, the ResolveField method will be called for any field on the object.

Next, the server checks for a method with the same name as the field. Field methods must have the following signature (with square brackets indicating optional elements):

func (foo *Foo) Bar([ctx context.Context,] [args ArgsType,] [sel *graphql.SelectionSet]) (ResultType[, error])

The ctx parameter will have a Context deriving from the one passed to Execute. The args parameter can be of type map[string]graphql.Value, S, or *S, where S is a struct type with fields for all of the arguments. See ConvertValueMap for a description of how this parameter is derived from the field arguments. The sel parameter is only passed to fields that return an object or list of objects type and permits the method to peek into what fields will be evaluated on its return value. This is useful for avoiding querying for data that won't be used in the response. The method must be exported, but otherwise methods are matched with fields ignoring case.

Lastly, if the object is a Go struct and the field takes no arguments, then the server will read the value from an exported struct field with the same name ignoring case.

Type Resolution

For abstract types, the server will first attempt to call a GraphQLType method as documented in the Typer interface. If that's not present, the Go type name will be matched with the GraphQL type with the same name ignoring case if present. Otherwise, type resolution fails.

Scalars

Go values will be converted to scalars in the result by trying the following in order:

  1. Call a method named IsGraphQLNull if present. If it returns true, then convert to null.

  2. Use the encoding.TextMarshaler interface if present.

  3. Examine the Go type and GraphQL types and attempt coercion.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"zombiezen.com/go/graphql-server/graphql"
)

// Query is the GraphQL object read from the server.
type Query struct {
	// GenericGreeting is a no-arguments field that is read directly.
	GenericGreeting string
}

// Greet is a field that takes arguments.
func (q *Query) Greet(args *GreetArgs) (string, error) {
	message := fmt.Sprintf("Hello, %s!", args.Subject)
	return message, nil
}

// GreetArgs are arguments passed to the Query.greet field. The arguments are
// validated through GraphQL's type system and converted into this struct before
// the Greet method is called.
type GreetArgs struct {
	Subject string
}

func main() {
	// Parse the GraphQL schema to establish type information. The schema
	// is usually a string constant in your Go server or loaded from your
	// server's filesystem.
	schema, err := graphql.ParseSchema(`
		type Query {
			genericGreeting: String!
			greet(subject: String!): String!
		}
	`, nil)
	if err != nil {
		log.Fatal(err)
	}

	// A *graphql.Server binds a schema to a Go value. The structure of
	// the Go type should reflect the GraphQL query type.
	queryObject := &Query{GenericGreeting: "Hiya!"}
	server, err := graphql.NewServer(schema, queryObject, nil)
	if err != nil {
		log.Fatal(err)
	}

	// Once created, a *graphql.Server can execute requests.
	response := server.Execute(context.Background(), graphql.Request{
		Query: `
			query($subject: String!) {
				genericGreeting
				greet(subject: $subject)
			}
		`,
		Variables: map[string]graphql.Input{
			"subject": graphql.ScalarInput("World"),
		},
	})

	// GraphQL responses can be serialized however you want. Typically,
	// you would use JSON, but this example displays the results directly.
	if len(response.Errors) > 0 {
		log.Fatal(response.Errors)
	}
	fmt.Println(response.Data.ValueFor("genericGreeting").Scalar())
	fmt.Println(response.Data.ValueFor("greet").Scalar())
}
Output:

Hiya!
Hello, World!
Example (Json)

GraphQL requests and response can be converted to JSON using the standard encoding/json package.

// Create a schema and a server.
server := newServer()

// Use json.Unmarshal to parse a GraphQL request from JSON.
var request graphql.Request
err := json.Unmarshal([]byte(`{
		"query": "{ genericGreeting }"
	}`), &request)
if err != nil {
	log.Fatal(err)
}

// Use json.Marshal to serialize a GraphQL server response to JSON.
// We use json.MarshalIndent here for easier display.
response := server.Execute(context.Background(), request)
responseJSON, err := json.MarshalIndent(response, "", "  ")
if err != nil {
	log.Fatal(err)
}

fmt.Println(string(responseJSON))
Output:

{
  "data": {
    "genericGreeting": "Hiya!"
  }
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConvertValueMap added in v0.4.0

func ConvertValueMap(dst interface{}, m map[string]Value) error

ConvertValueMap converts a map of GraphQL values into a Go value. dst must be a non-nil pointer to a struct with matching fields or a map[string]graphql.Value.

During conversion to a struct, the values in the map will be converted (as if by Convert) into the struct field with the same name, ignoring case. An error will be returned if a key in the map does not match exactly one field in the Go struct.

Conversion to a map[string]graphql.Value will simply copy the map.

Types

type Field

type Field struct {
	// Key is the response object key. This may not be the same as the field name
	// when aliases are used.
	Key string
	// Value is the field's value.
	Value Value
}

Field is a field in an object or input object.

type FieldRequest added in v0.4.0

type FieldRequest struct {
	Name      string
	Args      map[string]Value
	Selection *SelectionSet
}

FieldRequest holds the parameters for a field resolution.

type FieldResolver added in v0.4.0

type FieldResolver interface {
	ResolveField(ctx context.Context, req FieldRequest) (interface{}, error)
}

A type implementing FieldResolver controls how its fields are dispatched. If present, Server will prefer to use the ResolveField method to execute fields on an object. ResolveField must be safe to call from multiple goroutines.

type Input

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

Input is a typeless GraphQL value. The zero value is null.

func InputObject

func InputObject(obj map[string]Input) Input

InputObject returns a new input with the given scalar value.

func ListInput

func ListInput(list []Input) Input

ListInput returns a new input with the given scalar value.

func ScalarInput

func ScalarInput(s string) Input

ScalarInput returns a new input with the given scalar value.

func (Input) GoValue

func (in Input) GoValue() interface{}

GoValue dumps the input into one of the following Go types:

  • nil interface{} for null
  • string for scalars
  • []interface{} for lists
  • map[string]interface{} for objects

func (Input) MarshalJSON

func (in Input) MarshalJSON() ([]byte, error)

MarshalJSON converts the input into JSON. All scalars will be represented as strings.

func (*Input) UnmarshalJSON

func (in *Input) UnmarshalJSON(data []byte) error

UnmarshalJSON converts JSON into an input.

type Location

type Location struct {
	Line   int `json:"line"`
	Column int `json:"column"`
}

Location identifies a position in a GraphQL document. Line and column are 1-based.

func (Location) String

func (loc Location) String() string

String returns the location in the form "line:col".

type NullBoolean

type NullBoolean struct {
	Bool  bool
	Valid bool
}

NullBoolean represents a Boolean that may be null. The zero value is null.

func (NullBoolean) IsGraphQLNull

func (n NullBoolean) IsGraphQLNull() bool

IsGraphQLNull returns !n.Valid.

func (NullBoolean) MarshalText

func (n NullBoolean) MarshalText() ([]byte, error)

MarshalText marshals the boolean to "true" or "false". It returns an error if n.Valid is false.

func (NullBoolean) String

func (n NullBoolean) String() string

String returns "true", "false", or "null".

func (*NullBoolean) UnmarshalText

func (n *NullBoolean) UnmarshalText(text []byte) error

UnmarshalText unmarshals a "true" or "false" into the boolean.

type NullFloat

type NullFloat struct {
	Float float64
	Valid bool
}

NullFloat represents an Float that may be null. The zero value is null.

func (NullFloat) IsGraphQLNull

func (n NullFloat) IsGraphQLNull() bool

IsGraphQLNull returns !n.Valid.

func (NullFloat) MarshalText

func (n NullFloat) MarshalText() ([]byte, error)

MarshalText marshals the floating point number to a decimal representation (or scientific notation for large exponents). It returns an error if n.Valid is false.

func (NullFloat) String

func (n NullFloat) String() string

String returns the decimal representation (using scientific notation for large exponents) or "null".

func (*NullFloat) UnmarshalText

func (n *NullFloat) UnmarshalText(text []byte) error

UnmarshalText unmarshals a floating point or integer literal.

type NullInt

type NullInt struct {
	Int   int32
	Valid bool
}

NullInt represents an Int that may be null. The zero value is null.

func (NullInt) IsGraphQLNull

func (n NullInt) IsGraphQLNull() bool

IsGraphQLNull returns !n.Valid.

func (NullInt) MarshalText

func (n NullInt) MarshalText() ([]byte, error)

MarshalText marshals the integer to a decimal representation. It returns an error if n.Valid is false.

func (NullInt) String

func (n NullInt) String() string

String returns the decimal representation or "null".

func (*NullInt) UnmarshalText

func (n *NullInt) UnmarshalText(text []byte) error

UnmarshalText unmarshals a decimal integer.

type NullString

type NullString struct {
	S     string
	Valid bool
}

NullString represents a String that may be null. The zero value is null.

func (NullString) IsGraphQLNull

func (n NullString) IsGraphQLNull() bool

IsGraphQLNull returns !n.Valid.

func (NullString) MarshalText

func (n NullString) MarshalText() ([]byte, error)

MarshalText converts n.S to []byte. It returns an error if n.Valid is false.

func (NullString) String

func (n NullString) String() string

String returns n.S or "null".

func (*NullString) UnmarshalText

func (n *NullString) UnmarshalText(text []byte) error

UnmarshalText converts the byte slice to a string.

type Nullable

type Nullable interface {
	IsGraphQLNull() bool
}

Nullable defines the IsGraphQLNull method. IsGraphQLNull reports whether the receiver should be represented in GraphQL as null.

type OperationDetails added in v0.6.0

type OperationDetails struct {
	// SelectionSet is the top-level selection set.
	SelectionSet *SelectionSet

	// HasErrors will be true if the operation will return at least one error.
	HasErrors bool
}

OperationDetails holds information about a nearly completed request.

type OperationFinisher added in v0.6.0

type OperationFinisher interface {
	FinishOperation(ctx context.Context, details *OperationDetails) error
}

If a top-level object (like query or mutation) implements OperationFinisher, then its FinishOperation method will be called after all its fields are resolved. The details struct must not be modified or retained past the end of the call to FinishOperation. If an error is returned, then it will be added to the response's errors, but the data will still be returned to the client.

If the top-level object is shared between requests, then FinishOperation must be safe to call concurrently from multiple goroutines.

type OperationType

type OperationType int

OperationType represents the keywords used to declare operations.

const (
	QueryOperation OperationType = 1 + iota
	MutationOperation
	SubscriptionOperation
)

Types of operations.

func (OperationType) String

func (typ OperationType) String() string

String returns the keyword corresponding to the operation type.

type PathSegment

type PathSegment struct {
	Field     string
	ListIndex int
}

PathSegment identifies a field or array index in an output object.

func (PathSegment) MarshalJSON

func (seg PathSegment) MarshalJSON() ([]byte, error)

MarshalJSON converts the segment to a JSON integer or a JSON string.

func (PathSegment) String

func (seg PathSegment) String() string

String returns the segment's index or field name as a string.

func (*PathSegment) UnmarshalJSON

func (seg *PathSegment) UnmarshalJSON(data []byte) error

UnmarshalJSON converts JSON strings into field segments and JSON numbers into list index segments.

type Request

type Request struct {
	// Query is the GraphQL document text.
	Query string `json:"query"`
	// If ValidatedQuery is not nil, then it will be used instead of the Query field.
	ValidatedQuery *ValidatedQuery `json:"-"`
	// If OperationName is not empty, then the operation with the given name will
	// be executed. Otherwise, the query must only include a single operation.
	OperationName string `json:"operationName,omitempty"`
	// Variables specifies the values of the operation's variables.
	Variables map[string]Input `json:"variables,omitempty"`
}

Request holds the inputs for a GraphQL execution.

type Response

type Response struct {
	Data   Value            `json:"data"`
	Errors []*ResponseError `json:"errors,omitempty"`
}

Response holds the output of a GraphQL operation.

func (Response) MarshalJSON

func (resp Response) MarshalJSON() ([]byte, error)

MarshalJSON converts the response to JSON format.

type ResponseError

type ResponseError struct {
	Message   string        `json:"message"`
	Locations []Location    `json:"locations,omitempty"`
	Path      []PathSegment `json:"path,omitempty"`
}

ResponseError describes an error that occurred during the processing of a GraphQL operation.

func (*ResponseError) Error

func (e *ResponseError) Error() string

Error returns e.Message.

type Schema

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

Schema is a parsed set of type definitions.

func ParseSchema

func ParseSchema(source string, opts *SchemaOptions) (*Schema, error)

ParseSchema parses a GraphQL document containing type definitions. It is assumed that the schema is trusted.

func ParseSchemaFile added in v0.2.0

func ParseSchemaFile(path string, opts *SchemaOptions) (*Schema, error)

ParseSchemaFile parses the GraphQL file containing type definitions named by path. It is assumed that the schema is trusted.

func (*Schema) Validate

func (schema *Schema) Validate(query string) (*ValidatedQuery, []*ResponseError)

Validate parses and type-checks an executable GraphQL document.

type SchemaOptions added in v0.2.0

type SchemaOptions struct {
	// IgnoreDescriptions will strip descriptions from the schema as it is parsed.
	IgnoreDescriptions bool
}

SchemaOptions specifies how the schema source will be interpreted. nil is treated the same as the zero value.

type SelectedField

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

SelectedField is a field in a selection set.

func (*SelectedField) Arg

func (f *SelectedField) Arg(name string) Value

Arg returns the argument with the given name or a null Value if the argument doesn't exist.

func (*SelectedField) ForType added in v0.7.0

func (f *SelectedField) ForType(typeName string) bool

ForType reports whether the field would be selected on the given type.

func (*SelectedField) Name added in v0.5.0

func (f *SelectedField) Name() string

Name returns the name of the field. This may be different than the key used in the response when the query is using aliases.

func (*SelectedField) SelectionSet

func (f *SelectedField) SelectionSet() *SelectionSet

SelectionSet returns the field's selection set or nil if the field doesn't have one.

type SelectionSet

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

A SelectionSet is a collection of object fields that a client is requesting for the server to return. The zero value or nil is an empty set.

func (*SelectionSet) Field added in v0.5.0

func (set *SelectionSet) Field(i int) *SelectedField

Field returns the i'th field in the selection set. Field will panic if i is not in the range [0, set.Len()).

func (*SelectionSet) FieldsWithName

func (set *SelectionSet) FieldsWithName(name string) []*SelectedField

FieldsWithName returns the fields in the selection set with the given name. There may be multiple in the case of a field alias.

func (*SelectionSet) Has

func (set *SelectionSet) Has(name string) bool

Has reports whether the selection set includes the field with the given name.

The argument may contain dots to check for subfields. For example, Has("a.b") returns whether the selection set contains a field "a" whose selection set contains a field "b".

func (*SelectionSet) HasAny added in v0.4.0

func (set *SelectionSet) HasAny(names ...string) bool

HasAny reports whether the selection set includes fields with any of the given names. If no names are given, then HasAny returns false.

The argument may contain dots to check for subfields. For example, HasAny("a.b") returns whether the selection set contains a field "a" whose selection set contains a field "b".

func (*SelectionSet) Len added in v0.5.0

func (set *SelectionSet) Len() int

Len returns the number of fields in the selection set.

func (*SelectionSet) OnlyUses added in v0.3.0

func (set *SelectionSet) OnlyUses(names ...string) bool

OnlyUses returns true if and only if the selection set does not include fields beyond those given as arguments and __typename.

The arguments may contain dots to check for subfields. For example, "a.b" refers to a field named "b" inside the selection set of a field named "a".

type Server

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

Server manages execution of GraphQL operations.

func NewServer

func NewServer(schema *Schema, query, mutation interface{}) (*Server, error)

NewServer returns a new server that is backed by the given query object and optional mutation object. These must either be objects that follow the rules laid out in Field Resolution in the package documentation, or functions that return such objects. Functions may have up to two parameters: an optional context.Context followed by an optional *SelectionSet. The function may also have an error return.

Top-level objects may also implement the OperationFinisher interface. See the interface documentation for details.

func (*Server) Execute

func (srv *Server) Execute(ctx context.Context, req Request) (response Response)

Execute runs a single GraphQL operation. It is safe to call Execute from multiple goroutines.

func (*Server) Schema

func (srv *Server) Schema() *Schema

Schema returns the schema passed to NewServer. It is safe to call from multiple goroutines.

type Typer added in v0.7.0

type Typer interface {
	GraphQLType() string
}

A type implementing Typer controls which GraphQL type its value represents. GraphQLType must be safe to call from multiple goroutines. GraphQLType must return the same value for the duration of a request.

type ValidatedQuery

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

ValidatedQuery is a query that has been parsed and type-checked.

Example
server := newServer()

// You can use your server's schema to validate a query.
query, errs := server.Schema().Validate(`{ genericGreeting }`)
if len(errs) > 0 {
	log.Fatal(errs)
}

// You can pass the query to the server and it will execute it directly.
response := server.Execute(context.Background(), graphql.Request{
	ValidatedQuery: query,
})
if len(response.Errors) > 0 {
	log.Fatal(response.Errors)
}
fmt.Println(response.Data.ValueFor("genericGreeting").Scalar())
Output:

Hiya!

func (*ValidatedQuery) TypeOf

func (query *ValidatedQuery) TypeOf(operationName string) OperationType

TypeOf returns the type of the operation with the given name or zero if no such operation exists. If the operation name is empty and there is only one operation in the query, then TypeOf returns the type of that operation.

type Value

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

A Value is a GraphQL value. The zero value is an untyped null.

For more information on GraphQL types, see https://graphql.org/learn/schema/#type-system

func (Value) At

func (v Value) At(i int) Value

At returns v's i'th element. At panics if v is not a list or i is not in the range [0, v.Len()).

func (Value) Boolean

func (v Value) Boolean() bool

Boolean reports if v is a scalar with the value "true".

func (Value) Convert added in v0.4.0

func (v Value) Convert(dst interface{}) error

Convert converts a GraphQL value into a Go value. dst must be a non-nil pointer.

For scalars, Convert will first attempt to use the encoding.TextUnmarshaler interface, if present. Next, Convert will try to convert the scalar to the Go type. A Go string will use the scalar's value verbatim. Numeric types will be converted by parsing the scalar as a number. Boolean types will be converted from the scalars "true" and "false".

For enums, Convert behaves like scalars as described above, but if encoding.TextUnmarshaler is not implemented, then the enum may only be converted into a string.

For objects and input objects, the Go value must either be a struct or a map[string]graphql.Value. During conversion to a struct, the GraphQL value's fields will be converted (as if by Convert) into the struct field with the same name, ignoring case. An error will be returned if a field in the GraphQL value does not match exactly one field in the Go struct. Conversion to a map[string]graphql.Value will use the field keys and copy the values.

Lists will be converted into Go slices. Elements are converted using the same rules as Convert.

If the Go value is of type graphql.Value, then the Value is copied verbatim.

Null will be converted to the zero value of that type.

func (Value) Field

func (v Value) Field(i int) Field

Field returns v's i'th field. Field panics if v is not an object or i is not in the range [0, v.NumFields()).

func (Value) GoValue

func (v Value) GoValue() interface{}

GoValue dumps the value into one of the following Go types:

  • nil interface{} for null
  • string for scalars
  • []interface{} for lists
  • map[string]interface{} for objects
Example (Gocmp)

GoValue is often useful for testing with go-cmp.

ctx := context.Background()

// Set up a server and execute a query.
server := newServer()
response := server.Execute(ctx, graphql.Request{
	Query: `{ genericGreeting, greet(subject: "World") }`,
})
if len(response.Errors) > 0 {
	log.Fatal(response.Errors)
}

// GoValue can produce a map that is easy to compare with an expected value
// using go-cmp.
got := response.Data.GoValue()
want := map[string]interface{}{
	"genericGreeting": "Hiya!",
	"greet":           "Hello, World!",
}
if diff := cmp.Diff(want, got); diff == "" {
	fmt.Println("Got the expected greeting")
} else {
	fmt.Printf("data (-want +got):\n%s", diff)
}
Output:

Got the expected greeting

func (Value) IsNull

func (v Value) IsNull() bool

IsNull reports whether v is null.

func (Value) Len

func (v Value) Len() int

Len returns the number of elements in v. Len panics if v is not a list or null.

func (Value) MarshalJSON

func (v Value) MarshalJSON() ([]byte, error)

MarshalJSON converts the value to JSON.

func (Value) NumFields

func (v Value) NumFields() int

NumFields returns the number of fields in v. NumFields panics if v is not null, an object, or an input object.

func (Value) Scalar

func (v Value) Scalar() string

Scalar returns the string value of v if it is a scalar or the empty string otherwise.

func (Value) String added in v0.4.0

func (v Value) String() string

String serializes the value to GraphQL literal syntax.

func (Value) ValueFor

func (v Value) ValueFor(key string) Value

ValueFor returns the value of the field with the given key or the zero Value if v does not have the given key. ValueFor panics if v is not an object or input object.

Jump to

Keyboard shortcuts

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