v0.13.0 master v0.12.2 v0.11.3 v0.10.2 v0.9.3 v0.8.3


Using maps as changesets

Occasionally you need to distinguish presence from nil (undefined vs null). In gqlgen we do this using maps:

type Query {
	updateUser(id: ID!, changes: UserChanges!): User

type UserChanges {
	name: String
	email: String

Then in config set the backing type to map[string]interface{}

    model: "map[string]interface{}"

After running go generate you should end up with a resolver that looks like this:

func (r *queryResolver) UpdateUser(ctx context.Context, id int, changes map[string]interface{}) (*User, error) {
	u := fetchFromDb(id)
	/// apply the changes
	return u, nil

We often use the mapstructure library to directly apply these changesets directly to the object using reflection:

func ApplyChanges(changes map[string]interface{}, to interface{}) error {
	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		ErrorUnused: true,
		TagName:     "json",
		Result:      to,
		ZeroFields:  true,
		// This is needed to get mapstructure to call the gqlgen unmarshaler func for custom scalars (eg Date)
		DecodeHook: func(a reflect.Type, b reflect.Type, v interface{}) (interface{}, error) {
			if reflect.PtrTo(b).Implements(reflect.TypeOf((*graphql.Unmarshaler)(nil)).Elem()) {
				resultType := reflect.New(b)
				result := resultType.MethodByName("UnmarshalGQL").Call([]reflect.Value{reflect.ValueOf(v)})
				err, _ := result[0].Interface().(error)
				return resultType.Elem().Interface(), err

			return v, nil

	if err != nil {
		return err

	return dec.Decode(changes)