Skip to content

Commit d442938

Browse files
authored
Merge branch 'master' into patch-1
2 parents ba2b080 + d3a54d4 commit d442938

16 files changed

+511
-85
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,5 @@ For more complex examples, refer to the [examples/](https://github.com/graphql-g
7171
| [dataloader](https://github.com/nicksrandall/dataloader) | [Nick Randall](https://github.com/nicksrandall) | [DataLoader](https://github.com/facebook/dataloader) implementation in Go. |
7272

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

examples/crud/Readme.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
# Go GraphQL CRUD example
22

3-
Implementation create, read, update and delete on Go
3+
Implement create, read, update and delete on Go.
44

5-
To run the program, go to the directory
6-
`cd examples/crud`
5+
To run the program:
76

8-
Run the example
9-
`go run main.go`
7+
1. go to the directory: `cd examples/crud`
8+
2. Run the example: `go run main.go`
109

1110
## Create
11+
1212
`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}}`
1313

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

18-
Get product list
19-
`http://localhost:8080/product?query={list{id,name,info,price}}`
16+
* Get single product by id: `http://localhost:8080/product?query={product(id:1){name,info,price}}`
17+
* Get product list: `http://localhost:8080/product?query={list{id,name,info,price}}`
2018

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

2423
## Delete
24+
2525
`http://localhost:8080/product?query=mutation+_{delete(id:1){id,name,info,price}}`

examples/crud/main.go

+21-11
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,34 @@ import (
1010
"github.com/graphql-go/graphql"
1111
)
1212

13+
// Product contains information about one product
1314
type Product struct {
1415
ID int64 `json:"id"`
1516
Name string `json:"name"`
1617
Info string `json:"info,omitempty"`
1718
Price float64 `json:"price"`
1819
}
1920

20-
var products []Product
21+
var products = []Product{
22+
{
23+
ID: 1,
24+
Name: "Chicha Morada",
25+
Info: "Chicha morada is a beverage originated in the Andean regions of Perú but is actually consumed at a national level (wiki)",
26+
Price: 7.99,
27+
},
28+
{
29+
ID: 2,
30+
Name: "Chicha de jora",
31+
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)",
32+
Price: 5.95,
33+
},
34+
{
35+
ID: 3,
36+
Name: "Pisco",
37+
Info: "Pisco is a colorless or yellowish-to-amber colored brandy produced in winemaking regions of Peru and Chile (wiki)",
38+
Price: 9.95,
39+
},
40+
}
2141

2242
var productType = graphql.NewObject(
2343
graphql.ObjectConfig{
@@ -204,17 +224,7 @@ func executeQuery(query string, schema graphql.Schema) *graphql.Result {
204224
return result
205225
}
206226

207-
func initProductsData(p *[]Product) {
208-
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}
209-
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}
210-
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}
211-
*p = append(*p, product1, product2, product3)
212-
}
213-
214227
func main() {
215-
// Primary data initialization
216-
initProductsData(&products)
217-
218228
http.HandleFunc("/product", func(w http.ResponseWriter, r *http.Request) {
219229
result := executeQuery(r.URL.Query().Get("query"), schema)
220230
json.NewEncoder(w).Encode(result)

examples/sql-nullstring/README.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Go GraphQL SQL null string example
2+
3+
<a target="_blank" rel="noopener noreferrer" href="https://golang.org/pkg/database/sql/#NullString">database/sql Nullstring</a> implementation, with JSON marshalling interfaces.
4+
5+
To run the program, go to the directory
6+
`cd examples/sql-nullstring`
7+
8+
Run the example
9+
`go run main.go`
10+
11+
## sql.NullString
12+
13+
On occasion you will encounter sql fields that are nullable, as in
14+
15+
```sql
16+
CREATE TABLE persons (
17+
id INT PRIMARY KEY,
18+
name TEXT NOT NULL,
19+
favorite_dog TEXT -- this field can have a NULL value
20+
)
21+
```
22+
23+
For the struct
24+
25+
```golang
26+
import "database/sql"
27+
28+
type Person struct {
29+
ID int `json:"id" sql:"id"`
30+
Name string `json:"name" sql:"name"`
31+
FavoriteDog sql.NullString `json:"favorite_dog" sql:"favorite_dog"`
32+
}
33+
```
34+
35+
But `graphql` would render said field as an object `{{ false}}` or `{{Bulldog true}}`, depending on their validity.
36+
37+
With this implementation, `graphql` would render the null items as an empty string (`""`), but would be saved in the database as `NULL`, appropriately.
38+
39+
The pattern can be extended to include other `database/sql` null types.

examples/sql-nullstring/main.go

+252
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
package main
2+
3+
import (
4+
"database/sql"
5+
"encoding/json"
6+
"fmt"
7+
"github.com/graphql-go/graphql"
8+
"github.com/graphql-go/graphql/language/ast"
9+
"log"
10+
)
11+
12+
// NullString to be used in place of sql.NullString
13+
type NullString struct {
14+
sql.NullString
15+
}
16+
17+
// MarshalJSON from the json.Marshaler interface
18+
func (v NullString) MarshalJSON() ([]byte, error) {
19+
if v.Valid {
20+
return json.Marshal(v.String)
21+
}
22+
return json.Marshal(nil)
23+
}
24+
25+
// UnmarshalJSON from the json.Unmarshaler interface
26+
func (v *NullString) UnmarshalJSON(data []byte) error {
27+
var x *string
28+
if err := json.Unmarshal(data, &x); err != nil {
29+
return err
30+
}
31+
if x != nil {
32+
v.String = *x
33+
v.Valid = true
34+
} else {
35+
v.Valid = false
36+
}
37+
return nil
38+
}
39+
40+
// NewNullString create a new null string. Empty string evaluates to an
41+
// "invalid" NullString
42+
func NewNullString(value string) *NullString {
43+
var null NullString
44+
if value != "" {
45+
null.String = value
46+
null.Valid = true
47+
return &null
48+
}
49+
null.Valid = false
50+
return &null
51+
}
52+
53+
// SerializeNullString serializes `NullString` to a string
54+
func SerializeNullString(value interface{}) interface{} {
55+
switch value := value.(type) {
56+
case NullString:
57+
return value.String
58+
case *NullString:
59+
v := *value
60+
return v.String
61+
default:
62+
return nil
63+
}
64+
}
65+
66+
// ParseNullString parses GraphQL variables from `string` to `CustomID`
67+
func ParseNullString(value interface{}) interface{} {
68+
switch value := value.(type) {
69+
case string:
70+
return NewNullString(value)
71+
case *string:
72+
return NewNullString(*value)
73+
default:
74+
return nil
75+
}
76+
}
77+
78+
// ParseLiteralNullString parses GraphQL AST value to `NullString`.
79+
func ParseLiteralNullString(valueAST ast.Value) interface{} {
80+
switch valueAST := valueAST.(type) {
81+
case *ast.StringValue:
82+
return NewNullString(valueAST.Value)
83+
default:
84+
return nil
85+
}
86+
}
87+
88+
// NullableString graphql *Scalar type based of NullString
89+
var NullableString = graphql.NewScalar(graphql.ScalarConfig{
90+
Name: "NullableString",
91+
Description: "The `NullableString` type repesents a nullable SQL string.",
92+
Serialize: SerializeNullString,
93+
ParseValue: ParseNullString,
94+
ParseLiteral: ParseLiteralNullString,
95+
})
96+
97+
/*
98+
CREATE TABLE persons (
99+
favorite_dog TEXT -- is a nullable field
100+
);
101+
102+
*/
103+
104+
// Person noqa
105+
type Person struct {
106+
Name string `json:"name"`
107+
FavoriteDog *NullString `json:"favorite_dog"` // Some people don't like dogs ¯\_(ツ)_/¯
108+
}
109+
110+
// PersonType noqa
111+
var PersonType = graphql.NewObject(graphql.ObjectConfig{
112+
Name: "Person",
113+
Fields: graphql.Fields{
114+
"name": &graphql.Field{
115+
Type: graphql.String,
116+
},
117+
"favorite_dog": &graphql.Field{
118+
Type: NullableString,
119+
},
120+
},
121+
})
122+
123+
func main() {
124+
schema, err := graphql.NewSchema(graphql.SchemaConfig{
125+
Query: graphql.NewObject(graphql.ObjectConfig{
126+
Name: "Query",
127+
Fields: graphql.Fields{
128+
"people": &graphql.Field{
129+
Type: graphql.NewList(PersonType),
130+
Args: graphql.FieldConfigArgument{
131+
"favorite_dog": &graphql.ArgumentConfig{
132+
Type: NullableString,
133+
},
134+
},
135+
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
136+
dog, dogOk := p.Args["favorite_dog"].(*NullString)
137+
people := []Person{
138+
Person{Name: "Alice", FavoriteDog: NewNullString("Yorkshire Terrier")},
139+
// `Bob`'s favorite dog will be saved as null in the database
140+
Person{Name: "Bob", FavoriteDog: NewNullString("")},
141+
Person{Name: "Chris", FavoriteDog: NewNullString("French Bulldog")},
142+
}
143+
switch {
144+
case dogOk:
145+
log.Printf("favorite_dog from arguments: %+v", dog)
146+
dogPeople := make([]Person, 0)
147+
for _, p := range people {
148+
if p.FavoriteDog.Valid {
149+
if p.FavoriteDog.String == dog.String {
150+
dogPeople = append(dogPeople, p)
151+
}
152+
}
153+
}
154+
return dogPeople, nil
155+
default:
156+
return people, nil
157+
}
158+
},
159+
},
160+
},
161+
}),
162+
})
163+
if err != nil {
164+
log.Fatal(err)
165+
}
166+
query := `
167+
query {
168+
people {
169+
name
170+
favorite_dog
171+
}
172+
}`
173+
queryWithArgument := `
174+
query {
175+
people(favorite_dog: "Yorkshire Terrier") {
176+
name
177+
favorite_dog
178+
}
179+
}`
180+
r1 := graphql.Do(graphql.Params{
181+
Schema: schema,
182+
RequestString: query,
183+
})
184+
r2 := graphql.Do(graphql.Params{
185+
Schema: schema,
186+
RequestString: queryWithArgument,
187+
})
188+
if len(r1.Errors) > 0 {
189+
log.Fatal(r1)
190+
}
191+
if len(r2.Errors) > 0 {
192+
log.Fatal(r1)
193+
}
194+
b1, err := json.MarshalIndent(r1, "", " ")
195+
b2, err := json.MarshalIndent(r2, "", " ")
196+
if err != nil {
197+
log.Fatal(err)
198+
199+
}
200+
fmt.Printf("\nQuery: %+v\n", string(query))
201+
fmt.Printf("\nResult: %+v\n", string(b1))
202+
fmt.Printf("\nQuery (with arguments): %+v\n", string(queryWithArgument))
203+
fmt.Printf("\nResult (with arguments): %+v\n", string(b2))
204+
}
205+
206+
/* Output:
207+
Query:
208+
query {
209+
people {
210+
name
211+
favorite_dog
212+
}
213+
}
214+
215+
Result: {
216+
"data": {
217+
"people": [
218+
{
219+
"favorite_dog": "Yorkshire Terrier",
220+
"name": "Alice"
221+
},
222+
{
223+
"favorite_dog": "",
224+
"name": "Bob"
225+
},
226+
{
227+
"favorite_dog": "French Bulldog",
228+
"name": "Chris"
229+
}
230+
]
231+
}
232+
}
233+
234+
Query (with arguments):
235+
query {
236+
people(favorite_dog: "Yorkshire Terrier") {
237+
name
238+
favorite_dog
239+
}
240+
}
241+
242+
Result (with arguments): {
243+
"data": {
244+
"people": [
245+
{
246+
"favorite_dog": "Yorkshire Terrier",
247+
"name": "Alice"
248+
}
249+
]
250+
}
251+
}
252+
*/

0 commit comments

Comments
 (0)