graphql

package module
v1.0.11 Latest Latest
Warning

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

Go to latest
Published: Jun 27, 2020 License: MIT Imports: 19 Imported by: 0

README

graphql

easy way to build a GraphQL server with Go.

Introduces

The graphql library is dedicated to combining Go's types with GraphQL's type system. Including scalar type, interface type, object type, all will be mapped to the basic data type, interfaces and struct in Go. graphql was inspired by thunder open sourced by samsarahq, and learned how to use the built-in text scanner of Go in the graph-gopher/graphql-go library to lexer and parse the words. Welcome!

Getting Started

A simple example for building a GraphQL server.

package main

import (
	"github.com/shyptr/graphql"
	"github.com/shyptr/graphql/schemabuilder"
	"github.com/shyptr/graphql/system/introspection"
	"net/http"
)

type Identity int

const (
	Student Identity = iota
	Teacher
)

type Person struct {
	Name     string
	Identity Identity
}

var db = []*Person{
	{"john", Student},
	{"mark", Student},
	{"lisa", Teacher},
}

func RegisterPerson(schema *schemabuilder.Schema) {
    person:=schema.Object("person", Person{}, "each person has an identity, student or teacher")
    person.FieldFunc("age", func(source Person)int {
    		switch source.Name {
    		case "john":
    			return 15
    		case "mark":
    			return 17
    		case "lisa":
    			return 30
    		default:
    			return 0
    		}
    },"field which does not exist in struct, named age, return int")
}

func RegisterEnum(schema *schemabuilder.Schema) {
	schema.Enum("identity", Identity(0), map[string]Identity{
		"student": Student,
		"teacher": Teacher,
	}, "identity enum")
}

func RegisterOperations(schema *schemabuilder.Schema) {
	query := schema.Query()
	query.FieldFunc("all", func() []*Person {
		return db
	}, "get all person from db")
	query.FieldFunc("queryByName", func(args struct{ Name string }) []*Person {
		var persons []*Person
		for _, p := range db {
			if p.Name == args.Name {
				persons = append(persons, p)
			}
		}
		return persons
	}, "get person from db by name")
	query.FieldFunc("queryByIdentity", func(args struct{ Identity Identity }) []*Person {
		var persons []*Person
		for _, p := range db {
			if p.Identity == args.Identity {
				persons = append(persons, p)
			}
		}
		return persons
	}, "get person from db by identity")

	mutation := schema.Mutation()
	mutation.FieldFunc("add", func(args struct {
		Name     string
		Identity Identity
	}) {
		db = append(db, &Person{Name: args.Name, Identity: args.Identity})
	}, "add a person into db")
}

func main() {
	builder := schemabuilder.NewSchema()
	RegisterEnum(builder)
	RegisterPerson(builder)
	RegisterOperations(builder)

	schema := builder.MustBuild()
	introspection.AddIntrospectionToSchema(schema)
	http.Handle("/", graphql.GraphiQLHandler())
	http.Handle("/query", graphql.HTTPHandler(schema))
	http.ListenAndServe(":3000", nil)
}

In this example, we registered the Person object, enum and operation. For fields that only need to get field values from the source struct, we don't need to register with FieldFunc separately, graphql will extract this field value from the source and return it. For fields that do not exist in the struct, they must be registered with FieldFunc.

Enum Type

type EnumType int

const (
    one EnumType = iota
    two
    three
) 

schema.Enum("EnumName",EnumType(0),map[string]interface{}{
    "One":DescField{one,"one of enumType"},
    "Two":tow,
    "Three":three,
})

in this case, we use usually method in go to defined enum type, and given value with iota. Enum function receive name,type and value map to build a Enum type in GraphQL. for describe enum value, we defined a DescField which can provide description fro enum value.

Object Type


Scalar Type

schema.Scalar("NullString", sql.NullString{}, func(value interface{}, dest reflect.Value) error {
	v, ok := value.(string)
	if !ok {
		return errors.New("invalid type expected string")
	}
	dest.Set(reflect.ValueOf(sql.NullString{
		String: v,
		Valid:  false,
	}))
	return nil
}, "scalar type for sql.NullString")

Interface Type

type Named interface {
	GetName() string
}

type Dog struct {
	Name    string `graphql:"name"`
	Barks   bool   `graphql:"barks"`
	Mother  *Dog   `graphql:"mother"`
	Father  *Dog   `graphql:"father"`
	Progeny []*Dog `graphql:"progeny"`
}

func (d *Dog) GetName() string {
	return d.Name
}

NamedType := build.Interface("Named", new(Named), nil, "")
NamedType.FieldFunc("name", "GetName")

DogType := build.Object("Dog", Dog{})
DogType.InterfaceList(NamedType)

Union Type

type Pet struct {
	*Dog
	*Cat
}

type Dog struct {
	Name    string `graphql:"name"`
	Barks   bool   `graphql:"barks"`
	Mother  *Dog   `graphql:"mother"`
	Father  *Dog   `graphql:"father"`
	Progeny []*Dog `graphql:"progeny"`
}

type Cat struct {
	Name    string `graphql:"name"`
	Meows   bool   `graphql:"meows"`
	Mother  *Cat   `graphql:"mother"`
	Father  *Cat   `graphql:"father"`
	Progeny []*Cat `graphql:"progeny"`
}

build.Union("Pet", Pet{})

Example

starwars

simple

License

This project is licensed under the MIT License - see the LICENSE file for details

Acknowledgments

samsarahq - thunder

graph-gophers - graphql-go

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Ctx = &Context{
	Request:               nil,
	Writer:                nil,
	keys:                  nil,
	MaxDepth:              50,
	Logger:                log.New(os.Stderr, "", 0),
	useStringDescriptions: false,
	HandlersChain:         []HandlerFunc{},
	Error:                 nil,
	index:                 -1,
}
View Source
var URL = "query"

Functions

func GraphiQLHandler

func GraphiQLHandler(url ...string) http.HandlerFunc

func HTTPHandler

func HTTPHandler(schema *internal.Schema) http.Handler

HTTPHandler implements the handler required for executing the graphql queries and mutations

func HTTPSubHandler

func HTTPSubHandler(schema *internal.Schema, s *pubsub.Subscription) (http.Handler, func())

HTTPSubHandler implements the handler required for executing the graphql subscriptions

func MaxDepth

func MaxDepth(n int)

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

func SetLogger

func SetLogger(logger *log.Logger)

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

func Use

func Use(mm ...HandlerFunc)

func UseStringDescriptions

func UseStringDescriptions()

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.

Types

type Context

type Context struct {
	Request *http.Request
	Writer  *Resp

	MaxDepth int
	Logger   *log.Logger

	HandlersChain []HandlerFunc
	Error         errors.MultiError

	OperationName string
	Method        ast.OperationType
	// contains filtered or unexported fields
}

func GetContext

func GetContext(ctx context.Context) *Context

func (*Context) ClientIP

func (c *Context) ClientIP() string

ClientIP implements a best effort algorithm to return the real client IP, it parses X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy. Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.

func (*Context) Deadline

func (c *Context) Deadline() (deadline time.Time, ok bool)

func (*Context) Done

func (c *Context) Done() <-chan struct{}

func (*Context) Err

func (c *Context) Err() error

func (*Context) Next

func (ctx *Context) Next()

func (*Context) ServerError

func (c *Context) ServerError(msg string, code int)

func (*Context) Set

func (c *Context) Set(key, value interface{})

func (*Context) Value

func (c *Context) Value(key interface{}) interface{}

type Handler

type Handler struct {
	Schema   *internal.Schema
	Executor *execution.Executor
	// contains filtered or unexported fields
}

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type HandlerFunc

type HandlerFunc func(*Context)

type MiddlewareFunc

type MiddlewareFunc func() HandlerFunc

type Resp

type Resp struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

func (*Resp) Status

func (r *Resp) Status() int

func (*Resp) WriteHeader

func (r *Resp) WriteHeader(statusCode int)

type Response

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

Resp 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

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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