Skip to content

Commit

Permalink
Merge branch 'master' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-ramon committed Feb 5, 2020
2 parents ba2b080 + d3a54d4 commit d442938
Show file tree
Hide file tree
Showing 16 changed files with 511 additions and 85 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -71,5 +71,5 @@ For more complex examples, refer to the [examples/](https://github.com/graphql-g
| [dataloader](https://github.com/nicksrandall/dataloader) | [Nick Randall](https://github.com/nicksrandall) | [DataLoader](https://github.com/facebook/dataloader) implementation in Go. |

### Blog Posts
- [Golang + GraphQL + Relay](http://wehavefaces.net/)
- [Golang + GraphQL + Relay](https://wehavefaces.net/learn-golang-graphql-relay-1-e59ea174a902)

18 changes: 9 additions & 9 deletions examples/crud/Readme.md
@@ -1,25 +1,25 @@
# Go GraphQL CRUD example

Implementation create, read, update and delete on Go
Implement create, read, update and delete on Go.

To run the program, go to the directory
`cd examples/crud`
To run the program:

Run the example
`go run main.go`
1. go to the directory: `cd examples/crud`
2. Run the example: `go run main.go`

## Create

`http://localhost:8080/product?query=mutation+_{create(name:"Inca Kola",info:"Inca Kola is a soft drink that was created in Peru in 1935 by British immigrant Joseph Robinson Lindley using lemon verbena (wiki)",price:1.99){id,name,info,price}}`

## Read
Get single product by id
`http://localhost:8080/product?query={product(id:1){name,info,price}}`

Get product list
`http://localhost:8080/product?query={list{id,name,info,price}}`
* Get single product by id: `http://localhost:8080/product?query={product(id:1){name,info,price}}`
* Get product list: `http://localhost:8080/product?query={list{id,name,info,price}}`

## Update

`http://localhost:8080/product?query=mutation+_{update(id:1,price:3.95){id,name,info,price}}`

## Delete

`http://localhost:8080/product?query=mutation+_{delete(id:1){id,name,info,price}}`
32 changes: 21 additions & 11 deletions examples/crud/main.go
Expand Up @@ -10,14 +10,34 @@ import (
"github.com/graphql-go/graphql"
)

// Product contains information about one product
type Product struct {
ID int64 `json:"id"`
Name string `json:"name"`
Info string `json:"info,omitempty"`
Price float64 `json:"price"`
}

var products []Product
var products = []Product{
{
ID: 1,
Name: "Chicha Morada",
Info: "Chicha morada is a beverage originated in the Andean regions of Perú but is actually consumed at a national level (wiki)",
Price: 7.99,
},
{
ID: 2,
Name: "Chicha de jora",
Info: "Chicha de jora is a corn beer chicha prepared by germinating maize, extracting the malt sugars, boiling the wort, and fermenting it in large vessels (traditionally huge earthenware vats) for several days (wiki)",
Price: 5.95,
},
{
ID: 3,
Name: "Pisco",
Info: "Pisco is a colorless or yellowish-to-amber colored brandy produced in winemaking regions of Peru and Chile (wiki)",
Price: 9.95,
},
}

var productType = graphql.NewObject(
graphql.ObjectConfig{
Expand Down Expand Up @@ -204,17 +224,7 @@ func executeQuery(query string, schema graphql.Schema) *graphql.Result {
return result
}

func initProductsData(p *[]Product) {
product1 := Product{ID: 1, Name: "Chicha Morada", Info: "Chicha morada is a beverage originated in the Andean regions of Perú but is actually consumed at a national level (wiki)", Price: 7.99}
product2 := Product{ID: 2, Name: "Chicha de jora", Info: "Chicha de jora is a corn beer chicha prepared by germinating maize, extracting the malt sugars, boiling the wort, and fermenting it in large vessels (traditionally huge earthenware vats) for several days (wiki)", Price: 5.95}
product3 := Product{ID: 3, Name: "Pisco", Info: "Pisco is a colorless or yellowish-to-amber colored brandy produced in winemaking regions of Peru and Chile (wiki)", Price: 9.95}
*p = append(*p, product1, product2, product3)
}

func main() {
// Primary data initialization
initProductsData(&products)

http.HandleFunc("/product", func(w http.ResponseWriter, r *http.Request) {
result := executeQuery(r.URL.Query().Get("query"), schema)
json.NewEncoder(w).Encode(result)
Expand Down
39 changes: 39 additions & 0 deletions examples/sql-nullstring/README.md
@@ -0,0 +1,39 @@
# Go GraphQL SQL null string example

<a target="_blank" rel="noopener noreferrer" href="https://golang.org/pkg/database/sql/#NullString">database/sql Nullstring</a> implementation, with JSON marshalling interfaces.

To run the program, go to the directory
`cd examples/sql-nullstring`

Run the example
`go run main.go`

## sql.NullString

On occasion you will encounter sql fields that are nullable, as in

```sql
CREATE TABLE persons (
id INT PRIMARY KEY,
name TEXT NOT NULL,
favorite_dog TEXT -- this field can have a NULL value
)
```

For the struct

```golang
import "database/sql"

type Person struct {
ID int `json:"id" sql:"id"`
Name string `json:"name" sql:"name"`
FavoriteDog sql.NullString `json:"favorite_dog" sql:"favorite_dog"`
}
```

But `graphql` would render said field as an object `{{ false}}` or `{{Bulldog true}}`, depending on their validity.

With this implementation, `graphql` would render the null items as an empty string (`""`), but would be saved in the database as `NULL`, appropriately.

The pattern can be extended to include other `database/sql` null types.
252 changes: 252 additions & 0 deletions examples/sql-nullstring/main.go
@@ -0,0 +1,252 @@
package main

import (
"database/sql"
"encoding/json"
"fmt"
"github.com/graphql-go/graphql"
"github.com/graphql-go/graphql/language/ast"
"log"
)

// NullString to be used in place of sql.NullString
type NullString struct {
sql.NullString
}

// MarshalJSON from the json.Marshaler interface
func (v NullString) MarshalJSON() ([]byte, error) {
if v.Valid {
return json.Marshal(v.String)
}
return json.Marshal(nil)
}

// UnmarshalJSON from the json.Unmarshaler interface
func (v *NullString) UnmarshalJSON(data []byte) error {
var x *string
if err := json.Unmarshal(data, &x); err != nil {
return err
}
if x != nil {
v.String = *x
v.Valid = true
} else {
v.Valid = false
}
return nil
}

// NewNullString create a new null string. Empty string evaluates to an
// "invalid" NullString
func NewNullString(value string) *NullString {
var null NullString
if value != "" {
null.String = value
null.Valid = true
return &null
}
null.Valid = false
return &null
}

// SerializeNullString serializes `NullString` to a string
func SerializeNullString(value interface{}) interface{} {
switch value := value.(type) {
case NullString:
return value.String
case *NullString:
v := *value
return v.String
default:
return nil
}
}

// ParseNullString parses GraphQL variables from `string` to `CustomID`
func ParseNullString(value interface{}) interface{} {
switch value := value.(type) {
case string:
return NewNullString(value)
case *string:
return NewNullString(*value)
default:
return nil
}
}

// ParseLiteralNullString parses GraphQL AST value to `NullString`.
func ParseLiteralNullString(valueAST ast.Value) interface{} {
switch valueAST := valueAST.(type) {
case *ast.StringValue:
return NewNullString(valueAST.Value)
default:
return nil
}
}

// NullableString graphql *Scalar type based of NullString
var NullableString = graphql.NewScalar(graphql.ScalarConfig{
Name: "NullableString",
Description: "The `NullableString` type repesents a nullable SQL string.",
Serialize: SerializeNullString,
ParseValue: ParseNullString,
ParseLiteral: ParseLiteralNullString,
})

/*
CREATE TABLE persons (
favorite_dog TEXT -- is a nullable field
);
*/

// Person noqa
type Person struct {
Name string `json:"name"`
FavoriteDog *NullString `json:"favorite_dog"` // Some people don't like dogs ¯\_(ツ)_/¯
}

// PersonType noqa
var PersonType = graphql.NewObject(graphql.ObjectConfig{
Name: "Person",
Fields: graphql.Fields{
"name": &graphql.Field{
Type: graphql.String,
},
"favorite_dog": &graphql.Field{
Type: NullableString,
},
},
})

func main() {
schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"people": &graphql.Field{
Type: graphql.NewList(PersonType),
Args: graphql.FieldConfigArgument{
"favorite_dog": &graphql.ArgumentConfig{
Type: NullableString,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
dog, dogOk := p.Args["favorite_dog"].(*NullString)
people := []Person{
Person{Name: "Alice", FavoriteDog: NewNullString("Yorkshire Terrier")},
// `Bob`'s favorite dog will be saved as null in the database
Person{Name: "Bob", FavoriteDog: NewNullString("")},
Person{Name: "Chris", FavoriteDog: NewNullString("French Bulldog")},
}
switch {
case dogOk:
log.Printf("favorite_dog from arguments: %+v", dog)
dogPeople := make([]Person, 0)
for _, p := range people {
if p.FavoriteDog.Valid {
if p.FavoriteDog.String == dog.String {
dogPeople = append(dogPeople, p)
}
}
}
return dogPeople, nil
default:
return people, nil
}
},
},
},
}),
})
if err != nil {
log.Fatal(err)
}
query := `
query {
people {
name
favorite_dog
}
}`
queryWithArgument := `
query {
people(favorite_dog: "Yorkshire Terrier") {
name
favorite_dog
}
}`
r1 := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
})
r2 := graphql.Do(graphql.Params{
Schema: schema,
RequestString: queryWithArgument,
})
if len(r1.Errors) > 0 {
log.Fatal(r1)
}
if len(r2.Errors) > 0 {
log.Fatal(r1)
}
b1, err := json.MarshalIndent(r1, "", " ")
b2, err := json.MarshalIndent(r2, "", " ")
if err != nil {
log.Fatal(err)

}
fmt.Printf("\nQuery: %+v\n", string(query))
fmt.Printf("\nResult: %+v\n", string(b1))
fmt.Printf("\nQuery (with arguments): %+v\n", string(queryWithArgument))
fmt.Printf("\nResult (with arguments): %+v\n", string(b2))
}

/* Output:
Query:
query {
people {
name
favorite_dog
}
}
Result: {
"data": {
"people": [
{
"favorite_dog": "Yorkshire Terrier",
"name": "Alice"
},
{
"favorite_dog": "",
"name": "Bob"
},
{
"favorite_dog": "French Bulldog",
"name": "Chris"
}
]
}
}
Query (with arguments):
query {
people(favorite_dog: "Yorkshire Terrier") {
name
favorite_dog
}
}
Result (with arguments): {
"data": {
"people": [
{
"favorite_dog": "Yorkshire Terrier",
"name": "Alice"
}
]
}
}
*/

0 comments on commit d442938

Please sign in to comment.