Skip to main content
Version: 0.7.9

Go GraphQL SQL null string example

database/sql Nullstring 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

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

For the struct

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.

main.go
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.NullStringtype NullString struct {    sql.NullString}
// MarshalJSON from the json.Marshaler interfacefunc (v NullString) MarshalJSON() ([]byte, error) {    if v.Valid {        return json.Marshal(v.String)    }    return json.Marshal(nil)}
// UnmarshalJSON from the json.Unmarshaler interfacefunc (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" NullStringfunc 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 stringfunc 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 NullStringvar 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 noqatype Person struct {    Name        string      `json:"name"`    FavoriteDog *NullString `json:"favorite_dog"` // Some people don't like dogs ¯\_(ツ)_/¯}
// PersonType noqavar 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, "", "  ")    if err != nil {        log.Fatal(err)    }    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"      }    ]  }}*/