github.com/maeglindeveloper/gqlgen@v0.13.1-0.20210413081235-57808b12a0a0/docs/content/reference/changesets.md (about) 1 --- 2 linkTitle: Changesets 3 title: Using maps as changesets 4 description: Falling back to map[string]interface{} to allow for presence checks. 5 menu: { main: { parent: 'reference', weight: 10 } } 6 --- 7 8 Occasionally you need to distinguish presence from nil (undefined vs null). In gqlgen we do this using maps: 9 10 11 ```graphql 12 type Query { 13 updateUser(id: ID!, changes: UserChanges!): User 14 } 15 16 type UserChanges { 17 name: String 18 email: String 19 } 20 ``` 21 22 Then in config set the backing type to `map[string]interface{}` 23 ```yaml 24 models: 25 UserChanges: 26 model: "map[string]interface{}" 27 ``` 28 29 After running go generate you should end up with a resolver that looks like this: 30 ```go 31 func (r *queryResolver) UpdateUser(ctx context.Context, id int, changes map[string]interface{}) (*User, error) { 32 u := fetchFromDb(id) 33 /// apply the changes 34 saveToDb(u) 35 return u, nil 36 } 37 ``` 38 39 We often use the mapstructure library to directly apply these changesets directly to the object using reflection: 40 ```go 41 42 func ApplyChanges(changes map[string]interface{}, to interface{}) error { 43 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 44 ErrorUnused: true, 45 TagName: "json", 46 Result: to, 47 ZeroFields: true, 48 // This is needed to get mapstructure to call the gqlgen unmarshaler func for custom scalars (eg Date) 49 DecodeHook: func(a reflect.Type, b reflect.Type, v interface{}) (interface{}, error) { 50 if reflect.PtrTo(b).Implements(reflect.TypeOf((*graphql.Unmarshaler)(nil)).Elem()) { 51 resultType := reflect.New(b) 52 result := resultType.MethodByName("UnmarshalGQL").Call([]reflect.Value{reflect.ValueOf(v)}) 53 err, _ := result[0].Interface().(error) 54 return resultType.Elem().Interface(), err 55 } 56 57 return v, nil 58 }, 59 }) 60 61 if err != nil { 62 return err 63 } 64 65 return dec.Decode(changes) 66 } 67 ```