Когда пишете GraphQL API на Go, рано или поздно сталкиваетесь с проблемой N+1. Резолвер запрашивает пользователя для каждого поста отдельно — и вместо одного запроса к базе получаете сотню.
graph-gophers/dataloader решает именно это.Что делает библиотека
Она собирает отдельные запросы в батч и выполняет их одним вызовом. Параллельно кэширует результаты на время жизни загрузчика. Это реализация Facebook DataLoader для Go, с поддержкой дженериков начиная с v7.
Как это работает
Вы описываете одну батч-функцию, которая принимает список ключей и возвращает список результатов. Загрузчик сам накапливает ключи из параллельных вызовов
Load(), вызывает вашу функцию один раз и раздаёт ответы обратно через thunk.Thunk — это функция, которую возвращает
Load(). Она блокирует выполнение до тех пор, пока батч не завершится, и отдаёт значение или ошибку.Пример использования
Допустим, нужно загружать пользователей по ID. Без батчинга каждый резолвер делает свой запрос. С
dataloader все запросы в рамках одного окна объединяются:batchFn := func(ctx context.Context, ids []int) []*dataloader.Result[*User] {
users := fetchUsersByIDs(ctx, ids) // один запрос к БД
results := make([]*dataloader.Result[*User], len(ids))
for i, id := range ids {
results[i] = &dataloader.Result[*User]{Data: users[id]}
}
return results
}
loader := dataloader.NewBatchedLoader(batchFn)
// вызывается из разных горутин или резолверов
thunk := loader.Load(ctx, 42)
user, err := thunk()Все вызовы
Load() в одном временном окне попадут в один батч — fetchUsersByIDs выполнится один раз.По умолчанию используется встроенный in-memory кэш. Он рассчитан на короткий цикл жизни — один HTTP-запрос. Если кэш не нужен совсем, есть
NoCache:loader := dataloader.NewBatchedLoader(batchFn, dataloader.WithCache(&dataloader.NoCache[int, *User]{}))Можно передать свою реализацию интерфейса
Cache, например, на основе Redis.Если вы пишете GraphQL-сервер на Go и у вас есть резолверы, которые загружают связанные сущности — библиотека избавит от N+1 без ручного переписывания логики. Работает и без GraphQL: подходит для любых сценариев, где нужно объединять параллельные запросы за данными.
➡️ Репозиторий
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction