Я работаю разработчиком в компании АО «Центр Электронных Финансов».
Один и наших проектов — портал Государственных закупок Республики Казахстан — goszakup.gov.kz.
Год назад мы запустили большой проект — Унифицированные сервисы (OpenData).
Для реализации была использована методология RestAPI.
Сегодня я расскажу о новой версии наших сервисов и новом интерфейсе работы с ними.
Мы разработали и запустили 6 сервисов Открытых данных:
- Реестр участников
- Реестр недобросовестных поставщиков
- Реестр годовых планов
- Объявления о гос. закупках
- Реестр лотов
- Реестр договоров
Многие компании Казахстана уже подключаются и получают данных по данным сервисам.
Запуск Открытых данных позволил примерно на 40% снизить нагрузку базы данных за счет того, что компаниям не нужно писать различные парсера чтобы собирать данных о Государственных закупках. Достаточно пройти не сложный Квест :)
- написать запрос на получение токена доступа
- ознакомиться с документацией по сервисам на нашем портале goszakup.gov.kz/ru/developer/ows
- написать свой RestAPI клиент
Унифицированные сервисы — Новый подход
В связи с тем, что объемы данных по Закупкам Казахстана то работать с ними по методологии RestAPI не всегда удобно.
RestAPI дает возможность удобнее и быстрее получить данные чем парсинг сайта, но стандартный RestAPI не дает компаниям гибкости и для построения связи с объектами приходится получать сначала все данные и только потом строить связи между ними.
Мы запускаем 2-ю версию Унифицированных сервисов — ows.goszakup.gov.kz/v2.
По мимо расширения наборов данных мы расширяем и возможность работы с нашим API.
Теперь данные можно получить и по RestAPI и по новому интерфейсу — GraphQL.
ows.goszakup.gov.kz/v2/graphql
Описывать, что такое GraphQL я не буду, для этого вы можете ознакомиться со статьей aliksend — Что же такое этот GraphQL?.
Я расскажу с какие преимущества мы получили после запуска GraphQL:
- гибкость запросов;
- получение связанных объектов;
- полную типизацию запросов и ответов;
- новый интерфейс поиска данных.
Гибкость запросов
При простом запросе RestAPI вы получаете тот формат данных которые были заложены заранее.
При запросе к GraphQL вы получаете данные в том формате в котором нужно вам.
При запросе данных вы сами определяете формат данных которые нужны, например нужны ИД и
Номер договора
{
contract
{
id
contract_number_sys
}
}
В ответ получаем только эти данные:
{
"data": {
"contract": [
{
"id": 1,
"contract_number_sys": "номер_договора"
}
]
}
}
Ну такие запросы это самое легкое в реализации GraphQL. Компании получают возможность самим выбирать какие данные они хотят получить, при этом нам не надо вносить каких либо корректировок как если бы это было при работе с RestAPI, и при этом не надо получать избыточные данные которые не нужны.
Получение связанных объектов
Мы не остановились на том, чтобы повторить функционал RestAPI просто дав возможность частично выбирать данные.
Мы реализовали 2-ю особенность GraphQL — связи объектов.
Если получать данные по RestAPI чтобы получать данные по договору и по компании заказчику в договоре требовалось сначала получить данные из Реестра участников, а только потом получать данные из Реестра договоров и самим строить связь между объектами.
Теперь при работе с GraphQL не нужно выполнять полное получение данных по Реестру участников, достаточно запросить данные в интересующем вас формате:
{
contract
{
id
contract_number_sys
customer
{
name_ru
}
}
}
Тем самым один запросом мы получаем и данные по договору и данные по компании заказчику:
{
"data": {
"contract": [
{
"id": 1,
"contract_number_sys": "номер_договора",
"customer" : {
"name_ru": "Компания Мира"
}
}
]
}
}
И таких связей реализовали много, теперь компаниям для получения данных потребуется значительно меньше запросов для получения данных. При этом больше не надо догадываться как именно связаны друг с другом объекты, получать полные наборы данных чтобы связать их друг с другом.
Я попытался наглядно показать частично структуру связей которой получилось добиться.
Типизация запросов и ответов
Многие сторонники SOAP запросов всегда ставили самым главным плюсом — типизацию данных.
RestAPI в отличие SOAP не имеет описания своей структуры и вы заранее не знаете какой тип данных. Но GraphQL меняет все.
Теперь можно запросить у GraphQL данные по всей схеме данных и вы получите:
- Полное описание структуры данных;
- Описание какое поле какой имеет тип данных (Int, String, Boolean);
- Описание вложенных объектов и структуры полей вложенных объектов;
- Детальное описание например на русском языке для каждого поля;
- Получения уведомления, что какое-то поле получило флаг Deprecated с описанием.
Я для работы с GraphQL использую программу Insomnia REST Client — insomnia.rest
Она при работе с GraphQL получает всю структуру объектов и подсказывает при построении запроса.
Приведу в качестве примера несколько скриншотов.
1. Помощь в построении запросов т.к. программа получила полную структуру объектов
2. Подсказку по описания каждого поля с его типом данных и описанием
3. Подсказку если какое-то поле получило флаг — Deprecated
И все это становится возможным за счет того, что GraphQL имеет четкую типизацию всех объектов, всех полей и вы всегда заранее будете знать какой тип данных ожидаете получить.
Новый интерфейс поиска данных
И самое интересное я оставил на последок.
Все вроде бы классно, есть возможность строить свою структуру данных, есть связь с другими объектами, все данных типизированы. Но все же чего-то не хватает…
Не хватает возможности искать по этим данным.
Дна начало был реализован формат поиска с указанием параметров в самом запросе:
{
trd_buy(ref_buy_status_id: 1)
{
name_kz
name_ru
}
}
Но тут я столкнулся с рядом проблем:
- при большом количестве критериев поиска запрос становится просто не читаемым;
- невозможно определить массив при поиске данных для поиска по нескольким вариантам одного поля.
Чтобы упростить построение запросов и расширить возможность поиска я реализовал вложенные объекты для фильтрации данных.
Определяем в запросе переменную с указанием объекта фильтрации.
query($filter: TrdBuyFiltersInput){
trd_buy(filters: $filter)
{
name_kz
name_ru
}
}
Описываем сами параметры поиска данных:
{
"filter": {
"ref_buy_status_id": [1, 2]
}
}
И в результате мы получим все объявления которые имеют статусы 1 и 2.
В самом запросе указываем только структуру данных, а все параметры поиска уходят в передачу параметров где уже можем передавать и массивы данных для фильтрации по нескольким критериям.
При этом в самой схеме GraphQL мы все также имеем описание и такого объекта поиска:
Унифицированные сервисы — Версия 2.0:
Работают сервисы:
- Реестр участников
- Реестр объявлений
- Реестр договоров
- Реестр актов
- Реестр лотов
- Реестр годовых планов
- Реестр недобросовестных поставщиков
Мы запустили новый функционал, который во много раз упрощает работу с нашим API, имеет гибкую структуру запросов и возможность поиска данных по заданным критериям.
Мы не потеряли в скорости получения данных, а только сокращаем за счет этого количество запросов необходимых для получения данных.
Мы получили возможность предупреждать в схеме данных об отключенных или переименованных полях.
Мы планируем и далее развивать API и дать возможность так-же морфологического поиска данных по сервисам.
Автор: GHostly_FOX
Все это хорошо, но почти все запросы на чтение данных с портала (get-запросы), а как быть поставщику если нужно автоматически согласовать договор, который принят (одобрен) заказчиком и в статусе ожидания принятия (согласования) поставщиком. Вручную это делается подписанием ЭЦП, но таких договоров у поставщика может быть много и раз договор уже согласован заказчиком то поставщик обязан подписать все согласованные договоры от заказчика. Логично что поставщик хочет автоматизировать такой процесс. Но в документации API нет методов, которые позволяли бы сделать это. А смысла только на чтение данных get-запросами без возможности программно выполнять рутинные действия post-запросами мало.