Last active
January 29, 2018 06:52
-
-
Save tonyghita/71eecd71579ab2e2f00c04a8449d9251 to your computer and use it in GitHub Desktop.
Example integration of graphql-go resolvers and dataloader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type RootResolver struct { | |
UsersClient users.client | |
} | |
type UserResolver struct { | |
id string // We can forgo a user lookup in the case a field resolvers only needs the user's id (e.g. fetching a user's friends). | |
load dataloader.Thunk // call this function to load user for field resolution | |
} | |
// loadUsers gets passed to the request-specific dataloader as the batch function | |
func loadUsers(ctx context.Context, client users.Client) dataloader.BatchFunc { | |
return func(ids []string) []*dataloader.Result { | |
results := []*dataloader.Result{} | |
users, err := client.GetUsers(ctx, ids) | |
if err != nil { | |
errRes := &dataloader.Result{Error: err} | |
// The number of results must match the number of ids, | |
// so we return the same error for all keys. | |
for range ids { | |
results = append(results, errRes) | |
} | |
return results | |
} | |
// create a map of id -> result | |
m := map[string]*dataloader.Result{} | |
for _, u := range users.Results { | |
m[u.ID] = &dataloader.Result{Data: u} | |
} | |
// order result array by input id | |
for _, id := range ids { | |
var result *dataloader.Result | |
var ok bool | |
if result, ok = m[id]; !ok { | |
result = &dataloader.Result{ | |
Error: fmt.Errorf("Unable to find user with id %q", id), | |
} | |
} | |
results = append(results, result) | |
} | |
return results | |
} | |
} | |
// User resolves the GraphQL query | |
// { | |
// user(id: ID!) User | |
// } | |
func (r *RootResolver) User(args *struct{ ID graphql.ID }) *UserResolver { | |
id := string(args.ID) | |
loader := dataloader.NewBatchedLoader(loadUsers(context.TODO(), r.UsersClient)) | |
thunk := loader.Load(id) | |
return &UserResolver{ | |
id: id, | |
load: thunk, | |
} | |
} | |
// Email is an example user field resolver | |
func (r *UserResolver) Email() *string { | |
user, err := extract(r.load()) | |
if err != nil { | |
// TODO: I'm not yet sure how to combine errors like "User Not Found"... | |
// If we were to return that error for all | |
// For now we will just not populate the field. | |
return nil | |
} | |
return user.Email | |
} | |
// helper function to make working with thunk results nicer | |
func extract(r *dataloader.Result) (*models.User, error) { | |
if user, ok := r.Data.(*models.User); ok { | |
return user, r.Error | |
} | |
return nil, r.Error | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment