Skip to content

Commit

Permalink
Change pacakge name to yarql
Browse files Browse the repository at this point in the history
  • Loading branch information
mjarkk committed Apr 18, 2022
1 parent 012831b commit 692b01e
Show file tree
Hide file tree
Showing 26 changed files with 100 additions and 73 deletions.
83 changes: 55 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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" }
```
Expand All @@ -282,22 +296,23 @@ func (BazWImpl) ResolveBar() string { return "This is baz" }
<summary>Relay Node example</summary>
<br>

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 {
ID uint `gq:"-"` // ignored because of (User).ResolveId()
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
}
```
Expand All @@ -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{
Expand Down Expand Up @@ -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

Expand All @@ -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_

Expand All @@ -387,9 +410,12 @@ BenchmarkResolve-12 13246 83731 ns/op 1344 B/op 47 allocs/
<summary>Compared to other libraries</summary>
<br>

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
Expand All @@ -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)
2 changes: 1 addition & 1 deletion apollo_tracing.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package graphql
package yarql

import (
"encoding/json"
Expand Down
2 changes: 1 addition & 1 deletion copy_schema.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package graphql
package yarql

import (
"reflect"
Expand Down
4 changes: 2 additions & 2 deletions directives.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package graphql
package yarql

import (
"errors"
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions enums.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package graphql
package yarql

import (
"errors"
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion enums_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package graphql
package yarql

import (
"testing"
Expand Down
10 changes: 5 additions & 5 deletions examples/fiber/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
10 changes: 5 additions & 5 deletions examples/gin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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) {
Expand All @@ -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 {
Expand Down
12 changes: 6 additions & 6 deletions examples/relay/backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@ import (

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
graphql "github.com/mjarkk/yarql"
"github.com/mjarkk/yarql"
)

func main() {
app := fiber.New()

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,
},
Expand All @@ -37,5 +37,5 @@ func main() {
return c.Send(res)
})

app.Listen(":5000")
log.Fatal(app.Listen(":5500"))
}
Loading

0 comments on commit 692b01e

Please sign in to comment.