Я работаю разработчиком в компании АО «Центр Электронных Финансов».
Один и наших проектов — портал Государственных закупок Республики Казахстан — goszakup.gov.kz.

Год назад мы запустили большой проект — Унифицированные сервисы (OpenData).
Для реализации была использована методология RestAPI.

Сегодня я расскажу о новой версии наших сервисов и новом интерфейсе работы с ними.



Мы разработали и запустили 6 сервисов Открытых данных:

  • Реестр участников
  • Реестр недобросовестных поставщиков
  • Реестр годовых планов
  • Объявления о гос. закупках
  • Реестр лотов
  • Реестр договоров

Многие компании Казахстана уже подключаются и получают данных по данным сервисам.
Запуск Открытых данных позволил примерно на 40% снизить нагрузку базы данных за счет того, что компаниям не нужно писать различные парсера чтобы собирать данных о Государственных закупках. Достаточно пройти не сложный Квест :)

  1. написать запрос на получение токена доступа
  2. ознакомиться с документацией по сервисам на нашем портале goszakup.gov.kz/ru/developer/ows
  3. написать свой RestAPI клиент

Унифицированные сервисы — Новый подход


RestAPI дает возможность удобнее и быстрее получить данные чем парсинг сайта, но стандартный RestAPI не дает компаниям гибкости и для построения связи с объектами приходится получать сначала все данные и только потом строить связи между ними.
Для того чтобы получить данные по объявлению необходимо по RestAPI запросить сначала реестр объявлений, затем реестр лотов и в завершении реестр планов. Значит на получение одного объявления необходимо выполнить минимум 3 запроса, а если надо получить данных по 50 объявлениям потребуется минимум 101 запрос к RestAPI при условии, что в каждом объявлении будет по 1 лоту (1 на получение 50 объявлений, 50 на получение лотов, 50 на получение пунктов плана).

Мы нашли способ сократить это количество до Одного запроса!

Мы запускаем 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
  }
}

Но тут я столкнулся с рядом проблем:

  1. при большом количестве критериев поиска запрос становится просто не читаемым;
  2. невозможно определить массив при поиске данных для поиска по нескольким вариантам одного поля.

Чтобы упростить построение запросов, и расширить возможность поиска я реализовал вложенные объекты для фильтрации данных.

Определяем в запросе переменную с указанием объекта фильтрации.

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 и дать возможность также морфологического поиска данных по сервисам.

Комментарии (3)


  1. lovecode
    25.02.2019 17:44

    Приятно узнать что сервис такого уровня в Родном Казахстане начал публиковать GraphQL API.
    Расскажите:

    1. Какую технологию используете для разработки backend'а?
    2. Как защищаете API от «излишней гибкости» GraphQL?


    1. GHostly_FOX Автор
      25.02.2019 17:52

      1. PHP с библиотекой graphql-php

      2. На текущий момент нет проблем с «излишней гибкостью», в качестве хранения данных используется NoSQL хранилище. А проблема с так называемыми N+1 решена за счет использования буферов.

      Если хотите получить 50 объявлений с лотами и планами то к хранилищу будет отправлено всего 3 запроса.


  1. bankinobi
    26.02.2019 17:27

    Молодцы, далеко ушли от OeBS в кластере.