diff --git a/README.md b/README.md index c6c3dd5..dab58df 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ ![Banner](https://github.com/mjarkk/yarql/blob/main/banner.png?raw=true) -[![Go Reference](https://pkg.go.dev/badge/github.com/mjarkk/yarql.svg)](https://pkg.go.dev/github.com/mjarkk/yarql) [![Go Report Card](https://goreportcard.com/badge/github.com/mjarkk/yarql)](https://goreportcard.com/report/github.com/mjarkk/yarql) [![Coverage Status](https://coveralls.io/repos/github/mjarkk/go-graphql/badge.svg?branch=main)](https://coveralls.io/github/mjarkk/go-graphql?branch=main) +[![Go Reference](https://pkg.go.dev/badge/github.com/mjarkk/yarql.svg)](https://pkg.go.dev/github.com/mjarkk/yarql) +[![Go Report Card](https://goreportcard.com/badge/github.com/mjarkk/yarql)](https://goreportcard.com/report/github.com/mjarkk/yarql) +[![Coverage Status](https://coveralls.io/repos/github/mjarkk/go-graphql/badge.svg?branch=main)](https://coveralls.io/github/mjarkk/go-graphql?branch=main) # YarQL, A Graphql library for GoLang @@ -13,14 +15,18 @@ Just a different approach to making graphql servers in Go - Build on top of the [graphql spec 2021](https://spec.graphql.org/October2021/) - No code generators - [Only 1 dependency](go.mod) -- Easy to implement in many web servers, see the [gin](https://github.com/mjarkk/yarql/blob/main/examples/gin/main.go) and [fiber](https://github.com/mjarkk/yarql/blob/main/examples/fiber/main.go) examples +- Easy to implement in many web servers, see the + [gin](https://github.com/mjarkk/yarql/blob/main/examples/gin/main.go) and + [fiber](https://github.com/mjarkk/yarql/blob/main/examples/fiber/main.go) + examples - [File upload support](#file-upload) - Supports [Apollo tracing](https://github.com/apollographql/apollo-tracing) - [Fast](#Performance) ## Example -See the [/examples](https://github.com/mjarkk/yarql/tree/main/examples) folder for more examples +See the [/examples](https://github.com/mjarkk/yarql/tree/main/examples) folder +for more examples ```go package main @@ -48,7 +54,7 @@ func (QueryRoot) ResolvePosts() []Post { type MethodRoot struct{} func main() { - s := graphql.NewSchema() + s := yarql.NewSchema() err := s.Parse(QueryRoot{}, MethodRoot{}, nil) if err != nil { @@ -82,7 +88,9 @@ func main() { ### Defining a field -All fields names are by default changed to graphql names, for example `VeryNice` changes to `veryNice`. There is one exception to the rule when the second letter is also upper case like `FOO` will stay `FOO` +All fields names are by default changed to graphql names, for example `VeryNice` +changes to `veryNice`. There is one exception to the rule when the second letter +is also upper case like `FOO` will stay `FOO` In a struct: @@ -195,17 +203,19 @@ func (A) ResolveMe() (*User, error) { ### Context -You can add `*graphql.Ctx` to every resolver of func field to get more information about the request or user set properties +You can add `*yarql.Ctx` to every resolver of func field to get more information +about the request or user set properties ```go -func (A) ResolveMe(ctx *graphql.Ctx) User { +func (A) ResolveMe(ctx *yarql.Ctx) User { return ctx.Values["me"].(User) } ``` ### Optional fields -All types that might be `nil` will be optional fields, by default these fields are: +All types that might be `nil` will be optional fields, by default these fields +are: - Pointers - Arrays @@ -214,7 +224,8 @@ All types that might be `nil` will be optional fields, by default these fields a Enums can be defined like so -Side note on using enums as argument, It might return a nullish value if the user didn't provide a value +Side note on using enums as argument, It might return a nullish value if the +user didn't provide a value ```go // The enum type, everywhere where this value is used it will be converted to an enum in graphql @@ -228,7 +239,7 @@ const ( ) func main() { - s := graphql.NewSchema() + s := yarql.NewSchema() // The map key is the enum it's key in graphql // The map value is the go value the enum key is mapped to or the other way around @@ -247,8 +258,11 @@ func main() { Graphql interfaces can be created using go interfaces -This library needs to analyze all types before you can make a query and as we cannot query all types that implement a interface you'll need to help the library with this by calling `Implements` for every implementation. -If `Implements` is not called for a type the response value for that type when inside a interface will always be `null` +This library needs to analyze all types before you can make a query and as we +cannot query all types that implement a interface you'll need to help the +library with this by calling `Implements` for every implementation. If +`Implements` is not called for a type the response value for that type when +inside a interface will always be `null` ```go type QuerySchema struct { @@ -267,13 +281,13 @@ type BarWImpl struct{} // Implements hints this library to register BarWImpl // THIS MUST BE CALLED FOR EVERY TYPE THAT IMPLEMENTS InterfaceType -var _ = graphql.Implements((*InterfaceType)(nil), BarWImpl{}) +var _ = yarql.Implements((*InterfaceType)(nil), BarWImpl{}) func (BarWImpl) ResolveFoo() string { return "this is bar" } func (BarWImpl) ResolveBar() string { return "This is bar" } type BazWImpl struct{} -var _ = graphql.Implements((*InterfaceType)(nil), BazWImpl{}) +var _ = yarql.Implements((*InterfaceType)(nil), BazWImpl{}) func (BazWImpl) ResolveFoo() string { return "this is baz" } func (BazWImpl) ResolveBar() string { return "This is baz" } ``` @@ -282,11 +296,12 @@ func (BazWImpl) ResolveBar() string { return "This is baz" } Relay Node example
-For a full relay example see [examples/relay/backend/](./examples/relay/backend/) +For a full relay example see +[examples/relay/backend/](./examples/relay/backend/) ```go type Node interface { - ResolveId() (uint, graphql.AttrIsID) + ResolveId() (uint, yarql.AttrIsID) } type User struct { @@ -294,10 +309,10 @@ type User struct { Name string } -var _ = graphql.Implements((*Node)(nil), User{}) +var _ = yarql.Implements((*Node)(nil), User{}) // ResolveId implements the Node interface -func (u User) ResolveId() (uint, graphql.AttrIsID) { +func (u User) ResolveId() (uint, yarql.AttrIsID) { return u.ID, 0 } ``` @@ -308,14 +323,16 @@ func (u User) ResolveId() (uint, graphql.AttrIsID) { These directives are added by default: -- `@include(if: Boolean!)` _on Fields and fragments, [spec](https://spec.graphql.org/October2021/#sec--include)_ -- `@skip(if: Boolean!)` _on Fields and fragments, [spec](https://spec.graphql.org/October2021/#sec--skip)_ +- `@include(if: Boolean!)` _on Fields and fragments, + [spec](https://spec.graphql.org/October2021/#sec--include)_ +- `@skip(if: Boolean!)` _on Fields and fragments, + [spec](https://spec.graphql.org/October2021/#sec--skip)_ To add custom directives: ```go func main() { - s := graphql.NewSchema() + s := yarql.NewSchema() // Also the .RegisterEnum(..) method must be called before .Parse(..) s.RegisterDirective(Directive{ @@ -348,7 +365,10 @@ func main() { ### File upload -_NOTE: This is NOT [graphql-multipart-request-spec](https://github.com/jaydenseric/graphql-multipart-request-spec) tough this is based on [graphql-multipart-request-spec #55](https://github.com/jaydenseric/graphql-multipart-request-spec/issues/55)_ +_NOTE: This is NOT +[graphql-multipart-request-spec](https://github.com/jaydenseric/graphql-multipart-request-spec) +tough this is based on +[graphql-multipart-request-spec #55](https://github.com/jaydenseric/graphql-multipart-request-spec/issues/55)_ In your go code add `*multipart.FileHeader` to a methods inputs @@ -361,18 +381,21 @@ func (SomeStruct) ResolveUploadFile(args struct{ File *multipart.FileHeader }) s In your graphql query you can now do: ```gql - uploadFile(file: "form_file_field_name") +uploadFile(file: "form_file_field_name") ``` In your request add a form file with the field name: `form_file_field_name` ## Testing -There is a [pkg.go.dev mjarkk/go-graphql/tester](https://pkg.go.dev/github.com/mjarkk/yarql/tester) package available with handy tools for testing the schema +There is a +[pkg.go.dev mjarkk/go-graphql/tester](https://pkg.go.dev/github.com/mjarkk/yarql/tester) +package available with handy tools for testing the schema ## Performance -Below shows a benchmark of fetching the graphql schema (query parsing + data fetching) +Below shows a benchmark of fetching the graphql schema (query parsing + data +fetching) _Note: This benchmark also profiles the cpu and that effects the score by a bit_ @@ -387,9 +410,12 @@ BenchmarkResolve-12 13246 83731 ns/op 1344 B/op 47 allocs/ Compared to other libraries
-Injecting `resolver_benchmark_test.go > BenchmarkHelloWorldResolve` into [appleboy/golang-graphql-benchmark](https://github.com/appleboy/golang-graphql-benchmark) results in the following: +Injecting `resolver_benchmark_test.go > BenchmarkHelloWorldResolve` into +[appleboy/golang-graphql-benchmark](https://github.com/appleboy/golang-graphql-benchmark) +results in the following: -Take these results with a big grain of salt, i didn't use the last version of the libraries thus my result might be garbage compared to the others by now! +Take these results with a big grain of salt, i didn't use the last version of +the libraries thus my result might be garbage compared to the others by now! ```sh # go test -v -bench=Master -benchmem @@ -409,7 +435,8 @@ BenchmarkMjarkkGraphQLGoMaster-12 2560764 466.5 ns/op 80 B/op ## Alternatives -- [graph-gophers/graphql-go](https://github.com/graph-gophers/graphql-go) :heart: The library that inspired me to make this one +- [graph-gophers/graphql-go](https://github.com/graph-gophers/graphql-go) + :heart: The library that inspired me to make this one - [ccbrown/api-fu](https://github.com/ccbrown/api-fu) - [99designs/gqlgen](https://github.com/99designs/gqlgen) - [graphql-go/graphql](https://github.com/graphql-go/graphql) diff --git a/apollo_tracing.go b/apollo_tracing.go index 6db40f1..a23e983 100644 --- a/apollo_tracing.go +++ b/apollo_tracing.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "encoding/json" diff --git a/copy_schema.go b/copy_schema.go index 4235495..0c82c2c 100644 --- a/copy_schema.go +++ b/copy_schema.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "reflect" diff --git a/directives.go b/directives.go index f15eb24..cd3cadf 100644 --- a/directives.go +++ b/directives.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "errors" @@ -77,7 +77,7 @@ type DirectiveModifier struct { // RegisterDirective registers a new directive func (s *Schema) RegisterDirective(directive Directive) error { if s.parsed { - return errors.New("(*graphql.Schema).RegisterDirective() cannot be ran after (*graphql.Schema).Parse()") + return errors.New("(*yarql.Schema).RegisterDirective() cannot be ran after (*yarql.Schema).Parse()") } err := checkDirective(&directive) diff --git a/enums.go b/enums.go index 88f25de..f609252 100644 --- a/enums.go +++ b/enums.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "errors" @@ -56,7 +56,7 @@ func validEnumType(t reflect.Type) bool { // RegisterEnum registers a new enum type func (s *Schema) RegisterEnum(enumMap interface{}) (added bool, err error) { if s.parsed { - return false, errors.New("(*graphql.Schema).RegisterEnum() cannot be ran after (*graphql.Schema).Parse()") + return false, errors.New("(*yarql.Schema).RegisterEnum() cannot be ran after (*yarql.Schema).Parse()") } enum, err := registerEnumCheck(enumMap) diff --git a/enums_test.go b/enums_test.go index da42c45..ac5a1d2 100644 --- a/enums_test.go +++ b/enums_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "testing" diff --git a/examples/fiber/main.go b/examples/fiber/main.go index ccc133a..17ba7c5 100644 --- a/examples/fiber/main.go +++ b/examples/fiber/main.go @@ -5,26 +5,26 @@ import ( "mime/multipart" "github.com/gofiber/fiber/v2" - graphql "github.com/mjarkk/yarql" + yarql "github.com/mjarkk/yarql" ) func main() { app := fiber.New() - graphqlSchema := graphql.NewSchema() - err := graphqlSchema.Parse(QueryRoot{}, MethodRoot{}, nil) + schema := yarql.NewSchema() + err := schema.Parse(QueryRoot{}, MethodRoot{}, nil) if err != nil { log.Fatal(err) } app.All("/graphql", func(c *fiber.Ctx) error { - res, _ := graphqlSchema.HandleRequest( + res, _ := schema.HandleRequest( c.Method(), func(key string) string { return c.Query(key) }, func(key string) (string, error) { return c.FormValue(key), nil }, func() []byte { return c.Body() }, string(c.Request().Header.ContentType()), - &graphql.RequestOptions{ + &yarql.RequestOptions{ GetFormFile: func(key string) (*multipart.FileHeader, error) { return c.FormFile(key) }, Tracing: true, }, diff --git a/examples/gin/main.go b/examples/gin/main.go index 6a39180..824361b 100644 --- a/examples/gin/main.go +++ b/examples/gin/main.go @@ -7,14 +7,14 @@ import ( "sync" "github.com/gin-gonic/gin" - graphql "github.com/mjarkk/yarql" + "github.com/mjarkk/yarql" ) func main() { r := gin.Default() - graphqlSchema := graphql.NewSchema() - err := graphqlSchema.Parse(QueryRoot{}, MethodRoot{}, nil) + schema := yarql.NewSchema() + err := schema.Parse(QueryRoot{}, MethodRoot{}, nil) if err != nil { log.Fatal(err) } @@ -38,7 +38,7 @@ func main() { lock.Lock() defer lock.Unlock() - res, _ := graphqlSchema.HandleRequest( + res, _ := schema.HandleRequest( c.Request.Method, c.Query, func(key string) (string, error) { @@ -57,7 +57,7 @@ func main() { return requestBody }, c.ContentType(), - &graphql.RequestOptions{ + &yarql.RequestOptions{ GetFormFile: func(key string) (*multipart.FileHeader, error) { form, err := getForm() if err != nil { diff --git a/examples/relay/backend/main.go b/examples/relay/backend/main.go index 0cfba4c..b419864 100644 --- a/examples/relay/backend/main.go +++ b/examples/relay/backend/main.go @@ -6,7 +6,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" - graphql "github.com/mjarkk/yarql" + "github.com/mjarkk/yarql" ) func main() { @@ -14,20 +14,20 @@ func main() { app.Use(cors.New()) - graphqlSchema := graphql.NewSchema() - err := graphqlSchema.Parse(QueryRoot{}, MethodRoot{}, nil) + schema := yarql.NewSchema() + err := schema.Parse(QueryRoot{}, MethodRoot{}, nil) if err != nil { log.Fatal(err) } app.All("/graphql", func(c *fiber.Ctx) error { - res, _ := graphqlSchema.HandleRequest( + res, _ := schema.HandleRequest( c.Method(), func(key string) string { return c.Query(key) }, func(key string) (string, error) { return c.FormValue(key), nil }, func() []byte { return c.Body() }, string(c.Request().Header.ContentType()), - &graphql.RequestOptions{ + &yarql.RequestOptions{ GetFormFile: func(key string) (*multipart.FileHeader, error) { return c.FormFile(key) }, Tracing: true, }, @@ -37,5 +37,5 @@ func main() { return c.Send(res) }) - app.Listen(":5000") + log.Fatal(app.Listen(":5500")) } diff --git a/examples/relay/backend/schema.go b/examples/relay/backend/schema.go index 9ca8cdd..79f81b2 100644 --- a/examples/relay/backend/schema.go +++ b/examples/relay/backend/schema.go @@ -3,7 +3,7 @@ package main import ( "fmt" - graphql "github.com/mjarkk/yarql" + "github.com/mjarkk/yarql" ) // QueryRoot defines the entry point for all graphql queries @@ -15,7 +15,7 @@ type MethodRoot struct{} // Node defines the required relay Node interface // ref: https://relay.dev/docs/guides/graphql-server-specification/ type Node interface { - ResolveId() (uint, graphql.AttrIsID) + ResolveId() (uint, yarql.AttrIsID) } // Todo respresents a todo entry @@ -25,10 +25,10 @@ type Todo struct { Done bool } -var _ = graphql.Implements((*Node)(nil), Todo{}) +var _ = yarql.Implements((*Node)(nil), Todo{}) // ResolveId implements the Node interface -func (u Todo) ResolveId() (uint, graphql.AttrIsID) { +func (u Todo) ResolveId() (uint, yarql.AttrIsID) { return u.ID, 0 } diff --git a/grahql_types.go b/grahql_types.go index 84e73b8..7b97729 100644 --- a/grahql_types.go +++ b/grahql_types.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( h "github.com/mjarkk/yarql/helpers" diff --git a/implement_helpers.go b/implement_helpers.go index 6ace83a..f1c6718 100644 --- a/implement_helpers.go +++ b/implement_helpers.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "bytes" diff --git a/implement_helpers_test.go b/implement_helpers_test.go index ecbab30..559a209 100644 --- a/implement_helpers_test.go +++ b/implement_helpers_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "errors" diff --git a/inject_schema.go b/inject_schema.go index 95e693e..56ee03e 100644 --- a/inject_schema.go +++ b/inject_schema.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "log" diff --git a/interfaces.go b/interfaces.go index 3cb19c7..4a9aa96 100644 --- a/interfaces.go +++ b/interfaces.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "reflect" diff --git a/interfaces_test.go b/interfaces_test.go index 72d0f81..f5bb5d2 100644 --- a/interfaces_test.go +++ b/interfaces_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "reflect" diff --git a/parse.go b/parse.go index d6a9ef8..da6d644 100644 --- a/parse.go +++ b/parse.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "bytes" diff --git a/parse_benchmark_test.go b/parse_benchmark_test.go index de1c3a1..b422a85 100644 --- a/parse_benchmark_test.go +++ b/parse_benchmark_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "testing" diff --git a/parse_test.go b/parse_test.go index 6405ea8..26cddf0 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "reflect" diff --git a/readme_test.go b/readme_test.go index caa5833..0401973 100644 --- a/readme_test.go +++ b/readme_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "log" diff --git a/resolver.go b/resolver.go index 2ea8589..15dca44 100644 --- a/resolver.go +++ b/resolver.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "bytes" @@ -145,7 +145,7 @@ type ResolveOptions struct { // The result json is written to (*Schema).Result func (s *Schema) Resolve(query []byte, opts ResolveOptions) []error { if !s.parsed { - fmt.Println("CALL (*graphql.Schema).Parse() before resolve") + fmt.Println("CALL (*yarql.Schema).Parse() before resolve") return []error{errors.New("invalid setup")} } diff --git a/resolver_benchmark_test.go b/resolver_benchmark_test.go index 107f8dc..ceb9250 100644 --- a/resolver_benchmark_test.go +++ b/resolver_benchmark_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "log" diff --git a/resolver_test.go b/resolver_test.go index 29a33b4..a094131 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "bytes" diff --git a/tester/tester_test.go b/tester/tester_test.go index b63faeb..6ab14fd 100644 --- a/tester/tester_test.go +++ b/tester/tester_test.go @@ -3,7 +3,7 @@ package tester import ( "testing" - graphql "github.com/mjarkk/yarql" + "github.com/mjarkk/yarql" "github.com/mjarkk/yarql/assert" ) @@ -23,7 +23,7 @@ type BarSchemaType struct { type TesterMutationSchema struct{} func TestTester(t *testing.T) { - s := graphql.NewSchema() + s := yarql.NewSchema() err := s.Parse(TesterQuerySchema{}, TesterMutationSchema{}, nil) assert.NoError(t, err) diff --git a/type_rename.go b/type_rename.go index 2efa9ec..0fce280 100644 --- a/type_rename.go +++ b/type_rename.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "log" diff --git a/type_rename_test.go b/type_rename_test.go index 7d55aed..5bec7d4 100644 --- a/type_rename_test.go +++ b/type_rename_test.go @@ -1,4 +1,4 @@ -package graphql +package yarql import ( "reflect"