Giter Club home page Giter Club logo

go-graphql's Introduction

GraphQL

A powerful GraphQL server implementation for Golang. Its aim is to be the fastest GraphQL implementation.

$ cat test.go
package main

import (
	"fmt"

	"github.com/playlyfe/go-graphql"
)

func main() {
	schema := `
	## double hashed comments be parsed as descriptions and show up in
	## introspection queries
	interface Pet {
	    name: String
	}
	# this is an internal comment
	type Dog implements Pet {
	    name: String
	    woofs: Boolean
	}
	type Cat implements Pet {
	    name: String
	    meows: Boolean
	}
	type QueryRoot {
	    pets: [Pet]
	}
	`
	resolvers := map[string]interface{}{}
	resolvers["QueryRoot/pets"] = func(params *graphql.ResolveParams) (interface{}, error) {
		return []map[string]interface{}{
			{
				"__typename": "Dog",
				"name":       "Odie",
				"woofs":      true,
			},
			{
				"__typename": "Cat",
				"name":       "Garfield",
				"meows":      false,
			},
		}, nil
	}
	context := map[string]interface{}{}
	variables := map[string]interface{}{}
	executor, err := graphql.NewExecutor(schema, "QueryRoot", "", resolvers)
	executor.ResolveType = func(value interface{}) string {
		if object, ok := value.(map[string]interface{}); ok {
			return object["__typename"].(string)
		}
		return ""
	}
	query := `{
		pets {
			name
			... on Dog {
				woofs
			}
			... on Cat {
				meows
			}
		}
	}`
	result, err := executor.Execute(context, query, variables, "")
	if err != nil {
	    panic(err)
	}
	fmt.Printf("%v", result)
}

Benchmarks

Name                                 Repetitions   
BenchmarkGoGraphQLMaster-4             10000        230846 ns/op       29209 B/op        543 allocs/op
BenchmarkPlaylyfeGraphQLMaster-4       50000         27647 ns/op        3269 B/op         61 allocs/op

More

graphql-js master

wrk -t12 -c400 -d30s --timeout 10s "http://localhost:3002/graphql?query={hello}"
Running 30s test @ http://localhost:3002/graphql?query={hello}
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   219.79ms   80.35ms 613.69ms   78.38%
    Req/Sec   149.99     96.37   494.00     58.29%
  52157 requests in 30.05s, 9.96MB read
Requests/sec:   1735.60
Transfer/sec:    339.33KB

graphql-go master

wrk -t12 -c400 -d30s --timeout 10s "http://localhost:3003/graphql?query={hello}"
Running 30s test @ http://localhost:3003/graphql?query={hello}
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   134.97ms  163.47ms   1.85s    86.12%
    Req/Sec   372.46    236.09     1.58k    70.99%
  133607 requests in 30.05s, 18.35MB read
Requests/sec:   4445.99
Transfer/sec:    625.22KB

playlyfe/go-graphql master

wrk -t12 -c400 -d30s --timeout 10s "http://localhost:3003/graphql?query={hello}"
Running 30s test @ http://localhost:3003/graphql?query={hello}
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    34.89ms   43.72ms 518.00ms   87.58%
    Req/Sec     1.44k     0.90k    6.10k    81.35%
  514095 requests in 30.05s, 70.60MB read
Requests/sec:  17108.13
Transfer/sec:      2.35MB

TODO

Validator

License

Playlyfe GraphQL
http://dev.playlyfe.com/
Copyright(c) 2015-2016, Playlyfe IT Solutions Pvt. Ltd, [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

go-graphql's People

Contributors

codelingobot avatar hsyed avatar koenpunt avatar kumarharsh avatar luisjakon avatar mayank1791989 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-graphql's Issues

64-bit integers

Looks like ints are being casted as int32:

Value: int32(val),

A few lines above, ints are parsed as 64-bit though:

val, err := strconv.ParseInt(token.Val, 10, 64)

Why not leave everything as 64-bit? I am using this library and got an overflow of an int field that holds milliseconds since the epoch (Unix time * 1000), since that is bigger than 2^32

Project stewardship.

@atrniv @pyros2097 Great work on this project. It's very different from the other 3 GraphQL libraries (as well as the reference JS implementation). For my purposes I think it is the most suitable base to work off. I am working on a massively multi-tenant project and need to design a GraphQL engine that can manage hundreds of schemas and alter them at runtime.

@paralin is doing some interesting work with Magellan. I will need to add features like that to the work I am doing as well. I don't want to fork and maintain an internal flavour of GraphQL -- as I realistically wouldn't be able to open source it

The other 3 GraphQL projects don't have a mechanism for us to contribute realistically (correct me if I am wrong @paralin).

I am hoping @atrniv @pyros2097 you guys can spend some resources to make it possible for us to contribute back, get parity with the spec and steward extensions which would allow us to build more advanced GraphQL engines.

Was the library built to work this way as well?

After playing with the resolvers, I've noticed a thing about them: they don't seem to care about higher sources. For example, the following resolverrs returns the same thing:

resolvers["Query/store"] = func(params *graphql.ResolveParams) (interface{}, error) {
		return STORE, nil
	}

	resolvers["Store/teas"] = func(params *graphql.ResolveParams) (interface{}, error) {
		return STORE["teas"], nil
	}

and

resolvers["Query/store"] = func(params *graphql.ResolveParams) (interface{}, error) {
		return map[string][]map[string]interface{}{}, nil //returning empty type
	}

	resolvers["Store/teas"] = func(params *graphql.ResolveParams) (interface{}, error) {
		return STORE["teas"], nil
	}

As you can see, it only cares about the deepest resolver. Is this a good or bad practice? Can I use it this way or I should return full source for every resolver?

Failure to process Quoted and Triple-quoted Schema Descriptions

Currently, it is impossible to use schemas that use quotes to wrap descriptions.

It would be useful to update the lexer/parser code to properly handle quoted descriptions as per the current GraphQL specs.

Are there any plans to bring the code up to specs? Many thanks!

Godoc comments for public types and fields

Adding some descriptions to the types and fields exposed in this library would make it a lot more approachable. I am excited to start using it, but even understanding what is provided in the ResolveParams can require a lot of digging and reading code which doesn't need to be the case. I'd be happy to do some of this work but I am afraid that my understanding of the entire library is still limited so I am likely not the best person for the job.

Introspection: Skip/Include Directive Locations

Some newer apps complain about missing locations when placing an IntrospectionQuery request.

A quick fix is to add the following to the directive's definition object in the schema.go file:

"locations":   []string{"FIELD", "FRAGMENT_DEFINITION", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"},

Below is an example where it would be placed...

schema.go (lines 269-315)

result := map[string]interface{}{
  "queryType": executor.introspectType(params, queryRoot),
  "directives": []map[string]interface{}{
    {
      "name":        "skip",
      "description": "Conditionally exclude a field or fragment during execution",
      "args":        []map[string]interface{}{ ... },
      "locations":   []string{"FIELD", "FRAGMENT_DEFINITION", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"},
      "onOperation": false,
      "onField":     true,
      "onFragment":  true,
    },
    ...
  },
}

schema.go (lines 88-96)

const INTROSPECTION_SCHEMA = `
...
type __Directive {
  name: String!
  description: String
  args: [__InputValue!]!
  locations: [String!]!
  onOperation: Boolean!
  onFragment: Boolean!
  onField: Boolean!
}
...
`

Documentation

Is there any documentation?

Especially on using this with a router.

Hide __typename field

I have noticed that every object has __typename field in GraphiQL documentation
2017-01-05 15-48-01

I think this field is not supposed to be visible, confirm me if I wrong

Subscriptions

I'm using the library now in my code and quite like how robust it is.

Can we add subscriptions? They are supported in the GraphQL spec and Apollo client.

https://dev-blog.apollodata.com/graphql-subscriptions-in-apollo-client-9a2457f015fb#.lkud9zs7b

subscription comments($repoName: String!) {
  newComments(repoName: $repoName) {
    content
    postedBy {
      username
    }
    postedAt
  }
}

From the article:

Based on the subscription field name “newComments” and a mapping from subscription names to channels, the server subscribes to one or more pub/sub channels. When something is published to one of these channels, the server runs the GraphQL query specified in the subscription and sends a full new result to the client.

My API surface idea for this is:

  • Add a net.Context argument to the resolve parameters. Use a context for each request. Similar to how go-grpc does it.
  • When a streaming call starts, create a context, and call the resolver functions. If a resolver function needs to return a stream of objects, instead of an array, it can return a chan interface{} channel.
  • The call continues to execute and finishes when one of (Context ended/closed, Result channel closed) occurs.

It's a little tough to work around the current pattern of Execute -> return result. I think the best way to handle this is to change the Execute function OR add another function that returns an object instead of a map[string]interface{}, and also accepts a root Context. In this object we can define how the result is shown. If it's a single map[string]interface{} result, set the result field of that object. Otherwise, set the resultChan fields on that object.

Date formatting

Currently a date is outputted using the default date format, which isn't parseable by javascript. Since javascript is a language wherein graphql is often interacted, with this is an issue IMO.

I worked around it by adding a custom time type which implements String() with the right date format.

type JsTime struct {
	time.Time
}

func (t JsTime) String() string {
	return t.Time.Format(time.RFC3339)
}

But optimally there should be a similar interface like the json one, with UnmarshalJSON and MarshalJSON.

Network context argument

It would be useful to pass a x/net/context.Context with Execute to allow canceling processing a query before it is done. This is especially useful for upcoming "Subscriptions"

Looking to make documentation, is params.Source the right way to handle nested fields with args?

I've been playing lately with this library and tested *graphql.ResolveParams to see what they offers. Since this library has no documentation, I'm looking to make one with basic and complex examples. For now to understant it how it works, I made a deep nested field that takes arguments for every sub-field and tried to implemented it. The code I ran looks like this:

package main

import (
	"net/http"

	"github.com/krypton97/GraphiQL"

	"github.com/krypton97/HandleGraphQL"
	"github.com/playlyfe/go-graphql"
)

var data = map[string]map[string]map[string]map[string]interface{}{
	"CNU": {
		"A": {
			"13": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"14": {
				"name":  "ale",
				"age":   13,
				"id":    "1",
				"grade": 1,
			},
			"15": {
				"name":  "al",
				"age":   19,
				"id":    "15",
				"grade": 76,
			},
			"16": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 0,
			},
		},
		"B": {
			"1": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"2": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"3": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"4": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
		},
		"C": {
			"412": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"413": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"415": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"416": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
		},
	},
	"ETTI": {
		"A": {
			"73": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"74": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"75": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"76": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
		},
		"B": {
			"23": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"34": {
				"name":  "tibi",
				"age":   20,
				"id":    "1",
				"grade": 1,
			},
			"25": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"56": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
		},
		"C": {
			"63": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"54": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"45": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
			"26": {
				"name":  "alex",
				"age":   19,
				"id":    "1",
				"grade": 1,
			},
		},
	},
}

func main() {
	schema := `
        type Query {
            students(school: String!): Class
        }

        type Class {
            class(class: String!): Student
        }

        type Student {
            student(id: ID!): Info
        }

        type Info {
            name: String
            age: Int
            id: String
            grade: Int
        }

    `
	resolvers := map[string]interface{}{}

	resolvers["Query/students"] = func(params *graphql.ResolveParams) (interface{}, error) {
		return data[params.Args["school"].(string)], nil
	}

	resolvers["Class/class"] = func(params *graphql.ResolveParams) (interface{}, error) {
		//fmt.Println(params.Source.(map[string]map[string]map[string]interface{})[params.Args["class"].(string)])
		return params.Source.(map[string]map[string]map[string]interface{})[params.Args["class"].(string)], nil
	}

	resolvers["Student/student"] = func(params *graphql.ResolveParams) (interface{}, error) {
		//fmt.Println(params.Source.(map[string]map[string]interface{})[params.Args["id"].(string)])
		return params.Source.(map[string]map[string]interface{})[params.Args["id"].(string)], nil
	}

	executor, err := graphql.NewExecutor(schema, "Query", "", resolvers)

	if err != nil {
		panic(err)
	}

	api := handler.New(&handler.Config{
		Executor: executor,
		Context:  "",
		Pretty:   true,
	})

	graphiql := graphiql.New("/graphql")

	http.Handle("/graphql", api)
	http.Handle("/", graphiql)
	http.ListenAndServe(":3000", nil)

}

After some work, this handles all the cases(excepting when a case cannot be found and return nulll). What I was really looking for was to collect all the arguments from the query. After playing with the params, I found that they provide the source for the upper field so I could have simpled used the source and the current arguments the resolver uses to return what needs. Since I wasn't that sure I made a benchmark where I implemented the resolvers firstly with the params.Source method and secondly with already known arguments that matches the query, here's the benchmark to run http://pastebin.com/raw/yuFikjk5.
The results was extremly different:

Params.Source-8           20000             68259 ns/op
KnownsIDs-8              200000             11634 ns/op

My final question is: Is there a method to get the query arguments directly or the only way to work with nested fields with arguments is to use the params.Source?

Improve Perf by using bytes

Its seems the graphql-go guys tested their library for performance and improved it by a factor of 30 by using bytes instead of string. So I checked the source and even we are using string which causes a lot of garbage collection it seems so I guess we also need to make the change later to seem how much perf we gain.

References:
graphql-go/graphql@065ab6b
graphql-go/graphql#119

We should take a look at this lib for improving the lexer,
https://github.com/tdewolff/buffer

Resolver Api with dynamic argument parsing

Was wondering if there is any appetite for enhancing the resolver api by enabling dynamic argument parsing for faster/safer coding.

Below is a rough (but working) example of how it could work...aided by some minimal external packages I quickly pulled together...

Any comments? Takers?

package main

import (
	"errors"
	"net/http"

	handler "github.com/krypton97/HandleGraphQL"
	router "github.com/luisjakon/playlyfe-router"
	graphiql "github.com/luisjakon/graphiql"
	graphql "github.com/playlyfe/go-graphql"
)

var (
	Salutations = map[string]string{
		"ro": "Salut",
		"en": "Hello",
		"fr": "Oui",
		"it": "Ciao",
		"de": "Hallo",
	}

	Students = map[string]map[string]interface{}{
		"1": {"__typename": "Student", "name": "alex", "age": 18, "id": "1"},
		"2": {"__typename": "Student", "name": "bob", "age": 19, "id": "2"},
		"3": {"__typename": "Student", "name": "john", "age": 20, "id": "3"},
	}
)

func main() {

	schema := `
type Student {
	name: String
	age: Int
}

type Query {
	hello: String
	salutation(lang: String!): String
	student(id: String!): Student
}	
`
	router := router.NewRouter()

	router.Register("Query/hello", QueryResolver.Hello)
	router.Register("Query/salutation", QueryResolver.Salutation)
	router.Register("Query/student", QueryResolver.StudentById)
	router.Register("Student/age", StudentResolver.Age)

	executor, err := graphql.NewExecutor(schema, "Query", "", router)
	if err != nil {
		panic(err)
	}

	api := handler.New(&handler.Config{
		Executor: executor,
		Context:  "",
		Pretty:   true,
	})

	http.Handle("/graphql", graphiql.Handler(api))
	http.ListenAndServe("localhost:3000", nil)
}

resolvers.go

// Resolvers
var (
	QueryResolver   = queryRes{}
	StudentResolver = studentRes{}
)

// Query Resolver
type queryRes struct{}

func (r queryRes) Hello(params *graphql.ResolveParams) (interface{}, error) {
	return "world", nil
}

func (r queryRes) Salutation(params *graphql.ResolveParams, args struct { Lang string }) (interface{}, error) {
	s := Salutations[args.Lang]
	if s == "" {
		return nil, errors.New("Unknown Language: " + args.Lang)
	}
	return s, nil
}

func (r queryRes) StudentById(params *graphql.ResolveParams, args struct { Id string }) (interface{}, error) {
	return Students[args.Id], nil
}

// Student Resolver
type studentRes struct{}

func (r studentRes) Age(params *graphql.ResolveParams) (interface{}, error) {
	if v, ok := params.Source.(int); ok {
		return v, nil
	}
	source := params.Source.(map[string]interface{})
	id := source["id"].(string)
	return Students[id]["age"].(int), nil
}

Project status in Readme.MD

Hi,

I really like your implementation that defines gql in text! I am hoping to use the package in our project but it's not clear to me what's the status of this package. Could you put the status in Readme.MD? e.g. It's production ready, beta, alpha or WIP, graphQL spec supported version?

Thanks!
Ming

The right way to handle the "/graphql" endpoint

I'm wondering if this library has a custom handler to set the endpoint. So far I've been using something like this
`http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
result, err := executor.Execute(context, r.URL.Query()["query"][0], variables, "")

	if err != nil {
		panic(err)
	}
	json.NewEncoder(w).Encode(result)
})`

,but I don't know how good will fit in production. If there's any handler that you're using guys would be nice add it to the library, ty :)

coerceEnum not working

When returning enum values, the executor forces a coerceString(...) on enums. However, the values do not always get translated to the proper string values. A quick fix for this is to modify the utils/coerce.go file as follows:

utils/coerce.go

func coerceString(value interface{}) string {
    ...
    default:
        ...
        if v, ok := interface{}(value).(fmt.Stringer); ok {
            return v.String(), true
        }
        return "", false
    }
}

func coerceEnum(value interface{}) string {
    ...
    default:
        ...
        if v, ok := interface{}(value).(fmt.Stringer); ok {
            return v.String(), true
        }
        return "", false
    }
}

This way any enum defined with a Stringer method can properly send back the results.

Example:

package enums

const (
    APPLE = Fruit(iota)
    BANANA
    STRAWBERRY
)

type Fruit int64

func (f Fruit) String() string {
    switch f {
    case APPLE:
        return "APPLE"
    case BANANA:
        return "BANANA"
    case STRAWBERRY:
        return "STRAWBERRY"
    }
    return ""
}

Unexpected behavior with graphql

I was playing with the API and after a couple of tries I've noticed an issue with the returning value. I'll post here what my code looks like:

`
package main

import (
"net/http"

"fastQL/graphiql"

"github.com/krypton97/HandleGraphQL"
"github.com/playlyfe/go-graphql"

)

//Tea type
type Tea struct {
name string
steepingTime int
}

//Teas type
type Teas []*Tea

func main() {

schema := `
type Tea {
	name: String
	steepingTime: Int
}

type QueryRoot {
	teas: [Tea]
}
`
resolvers := map[string]interface{}{}

resolvers["QueryRoot/teas"] = func(params *graphql.ResolveParams) (interface{}, error) {
	return []map[string]interface{}{
		{
			"__typename":   "Tea",
			"name":         "Earl Grey Blue Star",
			"steepingTime": 5,
		},

		{
			"__typename":   "Tea",
			"name":         "Milk Oolong",
			"steepingTime": 3,
		},
		{
			"__typename":   "Tea",
			"name":         "Gunpowder Golden Temple",
			"steepingTIme": 2,
		},
		{
			"__typename":   "Tea",
			"name":         "Assam Hatimara",
			"steepingTime": 5,
		},
	}, nil
}

executor, err := graphql.NewExecutor(schema, "QueryRoot", "", resolvers)

executor.ResolveType = func(value interface{}) string {
	if object, ok := value.(map[string]interface{}); ok {
		return object["__typename"].(string)
	}
	return ""
}

if err != nil {
	panic(err)
}

api := handler.New(&handler.Config{
	Executor: executor,
	Context:  "",
	Pretty:   true,
})

graphiql := graphiql.New("/graphql")

http.Handle("/graphql", api)
http.Handle("/query", graphiql)
http.ListenAndServe(":3000", nil)

}
`

Running the following code return the following:

`
{
"data": {
"teas": [
{
"name": "Earl Grey Blue Star",
"steepingTime": 5
},
{
"name": "Milk Oolong",
"steepingTime": 3
},
{
"name": "Gunpowder Golden Temple",
"steepingTime": null
},
{
"name": "Assam Hatimara",
"steepingTime": 5
}
]
}
}

`

As you can see, the third field returns a null value, but the rest of them are ok. I can't seem to figure it out, any reason for that?

Working with nested types

I've been trying to figure it out for my own, but nothing seems to work since I can't find any documentation... I have the following code where I try to implement a nested type graphql. The schema is working fine, but it returns null..

package main

import ( 
    "net/http"
    "github.com/krypton97/HandleGraphQL"
    "github.com/playlyfe/go-graphql"
)

var Salutations = map[string]string{ 
    "ro": "Salut", 
    "en": "Hello", 
    "fr": "Oui", 
    "it": "Ciao", 
    "de": "Hallo", 
}

var Students = map[string]map[string]interface{}{
     "1": { "typename": "Student", "name": "alex", "age": 18, "id": "1", }, 
     "2": { "typename": "Student", "name": "bob", "age": 19, "id": "2", }, 
     "3": { "__typename": "Student", "name": "john", "age": 20, "id": "3", }, 
}
 
func main() {
schema := `
type StudentInfo {
    name: String
    age: Int
    id: String
}

type Student{
    student(id: String!) : StudentInfo
}

type QueryRoot {
    hello: String
    salutation(lang: String!): String
    students: Student

}
`

resolvers := map[string]interface{}{}

resolvers["QueryRoot/hello"] = func(params *graphql.ResolveParams) (interface{}, error) {
    return "world", nil
}

resolvers["QueryRoot/salutation"] = func(params *graphql.ResolveParams) (interface{}, error) {
    return Salutations[params.Args["lang"].(string)], nil
}

resolvers["QueryRoot/students"] = func(params *graphql.ResolveParams) (interface{}, error) {
    return Students, nil
}

resolvers["Student"] = func(params *graphql.ResolveParams) (interface{}, error) {
    return Students[params.Args["id"].(string)], nil
}

executor, err := graphql.NewExecutor(schema, "QueryRoot", "", resolvers)

if err != nil {
    panic(err)
}

api := handler.New(&handler.Config{
    Executor: executor,
    Context:  "",
    Pretty:   true,
})

http.Handle("/graphql", api)
http.ListenAndServe(":3000", nil)
}

Looking to get this working out, but can't find out anything. I'd really appreciate if you guys could help me out :)

Integration with react-relay

I've been looking for some examples, but couldn't find anything. Since the docs lacks a bit, I was wondering if it could be used with react-relay. An workaround would be using the apollo-client which is compatible with every backend language, but is not as great as relay is...If there's anyone that managed to do so, I'd be gladful for any source code ;)

Bug with deep map resolvers!

Looking to implement pagination with this library and I found a strange bug. By strange I mean that the fields are recognised, but the value is not returned. This is the code I have:

package data

import "github.com/playlyfe/go-graphql"
import "fmt"

var courses = map[string]map[string][]map[string]interface{}{
	"linkConnection": {
		"edges": {
			{
				"cursor": "YXJyYXljb25uZWN0aW9uOjA=",
				"node": map[string]string{
					"id":        "507f191e810c19729de860ea",
					"title":     "RelayJS course",
					"url":       "https://relay.cool.com/",
					"createdAt": "1974-08-07T11:53:39.056Z",
				},
			},
			{
				"cursor": "YXJyYXljb25uZWN0aW9uOjE=",
				"node": map[string]string{
					"id":        "58bc29705d58db311b3d1d85",
					"title":     "test link",
					"url":       "test.com",
					"createdAt": "1974-08-07T11:53:39.055Z",
				},
			},
			{
				"cursor": "YXJyYXljb25uZWN0aW9uOjI=",
				"node": map[string]string{
					"id":        "507f1f77bcf86cd799439011",
					"title":     "ReactJS main course",
					"url":       "https://mlab.com/databases/mongo/",
					"createdAt": "1974-08-07T11:53:39.054Z",
				},
			},
		},
	},
}

var Schema = `

	type Query {
		node(id: ID!): Node
		store: Store
	}

	type Mutation {
		createLink(input: CreateLinkInput!): CreateLinkPayload
	}
	
	interface Node {
		id: ID!
	}

	type Store implements Node {
		id: ID!
		linkConnection(after: String, first: Int, before: String, last: Int, query: String): LinkConnection
	}

	input CreateLinkInput {
		title: String!
		url: String!
		clientMutationId: String
	}

	type CreateLinkPayload {
		linkEdge: LinkEdge
		store: Store
		clientMutationId: String
	}

	type Link {
		id: ID!
		title: String
		url: String
		createdAt: String
	}

	type LinkConnection {
		pageInfo: PageInfo!
		edges: [LinkEdge]
	}

	type LinkEdge {
		node: Link
		cursor: String!
	}
	
	type PageInfo {
		hasNextPage: Boolean!
		hasPreviousPage: Boolean!
		startCursor: String
		endCursor: String
	}
`

var Resolvers = map[string]interface{}{
	"Query/store": func(p *graphql.ResolveParams) (interface{}, error) {
		return courses, nil
	},
	"Store/linkConnection": func(p *graphql.ResolveParams) (interface{}, error) {
		return courses["linkConnection"], nil
	},
	"LinkConnection/edges": func(p *graphql.ResolveParams) (interface{}, error) {
		fmt.Println(courses["linkConnection"]["edges"][0]["node"])
		return courses["linkConnection"]["edges"], nil
	},
}

and the output on graphiql looks like this

{
  "data": {
    "store": {
      "linkConnection": {
        "edges": [
          {
            "cursor": "YXJyYXljb25uZWN0aW9uOjA=",
            "node": {
              "createdAt": null,
              "title": null,
              "url": null
            }
          },
          {
            "cursor": "YXJyYXljb25uZWN0aW9uOjE=",
            "node": {
              "createdAt": null,
              "title": null,
              "url": null
            }
          },
          {
            "cursor": "YXJyYXljb25uZWN0aW9uOjI=",
            "node": {
              "createdAt": null,
              "title": null,
              "url": null
            }
          }
        ]
      }
    }
  }
}

I might be doind it wrong, but since the fields are recognised, it makes me think about it..

Submitting Enums as Input Values Fails

I'm struggling with how to submit a query or mutation with an enum value as a parameter (e.g.).

query {
   getReservations(days: [MONDAY, TUESDAY, WEDNESDAY]) {
      id,
      customer {
         name,
         phone
      },
      time
   }
}

Is there any way this can be achieved? Any pointers and/or example(s) greatly appreciated!

Many many thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.