Skip to content

Instantly share code, notes, and snippets.

@tonyghita
Last active January 29, 2018 06:52
Show Gist options
  • Save tonyghita/71eecd71579ab2e2f00c04a8449d9251 to your computer and use it in GitHub Desktop.
Save tonyghita/71eecd71579ab2e2f00c04a8449d9251 to your computer and use it in GitHub Desktop.
Example integration of graphql-go resolvers and dataloader
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