gogqlgen

How to make gqlgen dataloader Loader.Load(key) return a function that returns an explicit data type instead of interface{}?


I want to use graph-gophers/dataloader to implement a dataloader for bulk-fetching users.

I have implemented it like below but I need to return an explicit data type, not an interface{}

Firstly, I create UserReader with function GetUsers by given dataloader.Keys:

type UserReader struct {
    userCtrl user.Controller // This is my user service for Create, Get, Delete user
}

func newUserReader(userCtrl user.Controller) UserReader {
    return UserReader{
        userCtrl: userCtrl,
    }
}

// GetUsers return a list of result by given keys
func (reader UserReader) GetUsers(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
    output := make([]*dataloader.Result, len(keys))
    for i, key := range keys {
        userID := key.String()
        
        // Get list of user by string userID
        result, err := reader.userCtrl.GetUsers(ctx, userID)
        output[i] = &dataloader.Result{Data: result, Error: err}
    }

    return output
}

And then, I create a Loader with function GetUser to get a user by userID, it won't request data from userCtrl more than 1 time in a single request.

type Loader struct {
    userLoader *dataloader.Loader
}

func newLoader(userCtrl user.Controller) Loader {
    userReader := newUserReader(userCtrl)
    return Loader{
        // Create new user loader with batch function
        userLoader: dataloader.NewBatchedLoader(userReader.GetUsers),
    }
}

// UserLoader returns the user loader
func (loader Loader) UserLoader() *dataloader.Loader {
    return loader.userLoader
}

// GetUser return a user by given parameter
func (loader impl) GetUser(ctx context.Context, userID string) (model.User, error) {
    // FromCtx gets a Loader from context if it exists, else nil.
    ctxLoader := FromCtx(ctx)
    
    // Thunk is function return value with interface{} type and error
    thunk := ctxLoader.UserLoader().Load(ctx, dataloader.StringKey(userID))
    result, err := thunk()
    if err != nil {
        return nil, err
    }
    return result.(model.User), nil // Convert datatype here!!!
}

And finally, It can be use

dataloader.FromCtx(ctx).GetUser(ctx, userID)

But I need userReader.getUsers to return an object containing model.user not interface{} because I don't want to convert the data type in the Loader.getUser function.

Is there a way to have dataloader.Result return a certain data type instead of the interface{}?


Solution

  • The v7 version of graph-gophers/dataloader supports generics. Specifically, the BatchFunc signature has changed so you can specify the resulting type.