Currently, we work on migration from REST API to GraphQL for our services. We build a GraphQL server on top of SQL database for that, using gqlgen library.

When we created our schema:

type MenuLink {
  id: ID!
  langcode: String!
  title: String!
  url: String!
  additional: String
}

And generated struct:

type MenuLink struct {
    ID         int            `json:"id"`
    Langcode   string         `json:"langcode"`
    Title      string         `json:"title"`
    URL        string         `json:"link__uri"`
    Additional string         `json:"additional"`
}

we faced error "Scan error on column index 2: unsupported Scan, storing driver.Value type <nil> into type *string", when we tried to extract data from SQL database table and assign it to the struct. This happened because "additional" column contains NULL values sometimes and scanner is unable to convert null values into string.

To solve this issue, we just have to use "sql.NullString" instead of "string". But how we can tell to gqlgen to generate structs with this type?

The answer is not so complicated.

First, we have to add a new custom scalar (let's name it as NullableString) to graphql schema and change String type for additional attribute to it:

type MenuLink {
  id: ID!
  langcode: String!
  title: String!
  url: String!
  additional: NullableString
}

scalar NullableString

After that we have build an external marshaler for this new type:

package scalars

import (
    "database/sql"

    "github.com/99designs/gqlgen/graphql"
)
// MarshalNullString returns graphql.Null if string is NULL. If not, then uses default string implementation. 
func MarshalNullString(ns sql.NullString) graphql.Marshaler {
    if !ns.Valid {
        return graphql.Null
    }
    return graphql.MarshalString(ns.String)
}


func UnmarshalNullString(v interface{}) (sql.NullString, error) {
    if v == nil {
        return sql.NullString{Valid: false, String: ""}, nil
    }

    s, err := graphql.UnmarshalString(v)
    return sql.NullString{String: s, Valid: err == nil}, err
}


Here I just created a new package with name scalars where we can keep our marshalers.

And then in .gqlgen.yml we have to point to our marshaler:

schema:
- schema.graphql
exec:
  filename: generated.go
model:
  filename: models/generated.gopackage: models
struct_tag: json
models:
  NullableString:
    model: gitlab.mfb.io/user/graphql_server/scalars.NullString
resolver:
  filename: resolver.gotype: Resolver

That's it! After that if you run

$ go run github.com/99designs/gqlgen

It will generate struct with *sql.NullString type:

type MenuLink struct {
    ID         int             `json:"id"`
    Langcode   string          `json:"langcode"`
    Title      string          `json:"title"`
    URL        string          `json:"link__uri"`
    Additional *sql.NullString `json:"additional"`
}