Инструкция: как сделать фильтрацию и пагинацию на GraphQL и Go
Привет! Меня зовут Артем и я занимаюсь архитектурой решений в Redmadrobot. По долгу службы мне приходится разбираться с разными технологиями и подходами. В этой инструкции я хотел бы показать, как реализовать фильтрацию и два вида пагинации, если у вас Go и вы планируете использовать GraphQL.
Мы уже довольно давно применяем Go для написания backend-сервисов, но обычно все ограничивается старым добрым REST-ом или gRPC. На одном из новых проектов было принято решение использовать GraphQL для клиентского API.
Эту технологию часто ругают за потенциально бесконечную глубину запросов, за проблему «N+1 запросов», отсутствие механизма пагинации «из коробки» и много за что еще. Для решения большинства этих проблем мы выбрали библиотеку gqlgen, которая умеет справляться с «N+1», бесконечной глубиной , и всем остальным. Кроме фильтрации и пагинации. О них и пойдет речь ниже.
Быстрое погружение в тему
В веб-дизайне и дизайне программного обеспечения под пагинацией понимают постраничный вывод информации, то есть показ ограниченной части информации на одной (веб)-странице (например, 10 результатов поиска или 20 форумных тредов). Она повсеместно используется в веб-приложениях для разбиения большого массива данных на странице и включает в себя навигационный блок для перехода на другие страницы — пагинатор.
Различают два вида пагинации:
- Нумерованные страницы;
- Последовательные страницы.
Есть еще подход с бесконечным скроллом, но это скорее UI-трюк, нежели какой-то отдельный вид пагинации.
Реализация нумерованных страниц
Этот подход вводит такие понятия, как limit и offset, которые хорошо известны любому знающему SQL человеку. Фактически, это самый простой и понятный способ сделать пагинацию. Для начала нужно создать необходимые модели в файле schema.graphqls:
Потом дополнить структуру Resolver в файле resolver.go строкой todos []*model.Todo, сгенерировать резолверы командой gqlgen generate и убедиться, что сгенерировался файл с резолверами schema.resolvers.go с пустым резолвером для списка Todo.
Теперь можно написать реализацию пагинации. Для простоты в примере использован обычный массив с несколькими моделями Todo.
Можно запустить сервер и убедиться, что он стал отзываться на запросы вида:
Подход хорош своей простотой, но на этом его преимущества заканчиваются и начинаются проблемы. Их корень в динамической природе современного веба, а нумерованные страницы заточены на статический контент. В результате этой несостыковки появляются пропуски и дублирования элементов. Для решения этих проблем и была придумана пагинация с помощью последовательных страниц, она же Cursor Based Pagination.
Реализация последовательных страниц
Пагинация с применением курсоров несколько сложнее, т.к. вводит ряд дополнительных понятий, характерных только для GraphQL.
Connection — поле, данные из которого нужно пагинировать, содержит в себе edge-ы;
- Edge — мета-информация об объекте списка. Cодержит в себе поля node и cursor;
- Node — объект списка, он же модель данных;
- PageInfo — параметры пагинации.
Более подробно об этих понятиях можно почитать здесь.
Для реализации этого подхода потребуется чуть больше кода и правок конфигов. В первую очередь необходимо убедиться, что файл gqlgen.yml содержит параметр autobind, смотрящий на каталог с моделями:
После этого необходимо создать go-модели для обеспечения механизмов пагинации. Для этого в каталоге с моделями можно создать файл model.go с таким содержанием:
А потом добавить соответствующие модели в schema.graphqls:
Теперь нужно дополнить структуру Resolver в файле resolver.go строкой users []*model.User, сгенерировать резолверы и убедиться, что они создались корректно:
Осталось добавить реализацию для UsersConnection и Edges. Здесь также для простоты реализации используется обычный массив с пользователями:
Можно запустить сервер и убедиться, что он реагирует на запросы с пагинацией через курсор:
В ответе должен быть json похожий на этот:
Реализация фильтрации
Фильтрация — довольно простая концепция, поэтому ее я оставил напоследок. Покажу на примере с моделью Todo, которую мы уже реализовали выше. Нужно добавить новый input-параметр в файл с моделями GraphQL и указать этот параметр в запросе:
В зависимости от задачи тут может быть сколько угодно полей, по которым можно фильтровать, но не стоит выходить за пределы разумного. Далее нужно выполнить повторную генерацию моделей и написать реализацию:
Теперь можно проверить работоспособность фильтрации:
Заключение
Как видно, ничего сложно в реализации механизмов пагинации и фильтрации на GraphQL нет. При этом все еще хотелось бы иметь эти механизмы доступными «из коробки», а не писать каждый раз руками.
Тестовый проект, в котором уже реализованы все эти концепции можно найти здесь.
Полезные ссылки
Сервис приостановил работу в конце февраля 2025 года.
Когда-то я работал в СтеклоДоме и из пары человек мы быстро выросли в инхаус на 10+ человек.
Нанимаем верстальщика. Посмотрели анкеты, тестовые задачи. Назначаем встречу. Приходим я и разработчик Антон. На месте дожидается HR. Встреча, допустим, в 16-00.
Сидим, заготовили вопросы, ждём. 16-00. Тишина. 16-10 тишина.
Девелопмент — это не просто возведение зданий, а комплексный процесс, включающий юридические, финансовые и административные аспекты. Ключ к успешному строительному проекту — не только выбор подрядчиков и грамотное проектирование, но и своевременное получение всех необходимых разрешений. Ошибки на этом этапе могут привести к затягиванию сроков и фин…
Когда санкции похоронили мой магазин, я считал каждую копейку. А потом узнал про каналы — и теперь мои посты читают 50.000 человек в день
Стоит $599.
Правительство Таиланда предупредило о возможных повторных толчках.
Выпускники PR-стажировки by ITCOMMS из Узбекистана, Молдовы и Казахстана — о том, как программа помогает систематизировать знания и выйти из режима «самоучкин».
В январе 2025 года OpenAI, Oracle и SoftBank создали компанию Stargate для строительства дата-центров в стране.
Несколько тезисов из интервью РБК с главой сети в Центральном федеральном округе Еленой Захаренко.
Контролировать платежи будет Роскомнадзор.
Как сделать фильтрацию и пагинацию на GraphQL и Go?Использовать Prisma - https://github.com/prisma/prisma-client-go
Prisma это клиент. В статье идет речь о реализации серверной части.
Если у вас база на PostgresSql то я бы на Hasura взглянул бы. Быстрая, простая, удобная.
Благодарю, добавил себе в закладки. Но у нас на этом проекте не PostgreSQL, поэтому рассмотрю на будущее.
Для простоты в примере использован обычный массив с несколькими моделями TodoЗдесь также для простоты реализации используется обычный массив с пользователями
[]*model.User и []*model.Todo - это срезы
Если совсем занудствовать, то можно сказать, что слайсы это абстракция над массивами, но спасибо за ваше уточнение.
Сообщение удалено