Декларативное объединение данных из PostgreSQL на GraphQL без написания SQL

SQL является основным способом взаимодействия с реляционными базами данных, такими как PostgreSQL. При создании API, REST или GraphQL, для базы данных PostgreSQL необходимо знать SQL для получения и передачи данных. Часто разработчикам помогают инструменты ORM (Object Relational Mapping) для взаимодействия с базой данных, и эти инструменты абстрагируют некоторые знания SQL, необходимые для создания API.

Даже если эти инструменты помогут вам начать работу, они не помогут вам создать эффективный SQL. Вы будете не первым разработчиком, написавшим неэффективный запрос, который перегружает базу данных или выводит ее из строя. В StepZen мы создали инструменты и сервисы для декларативного создания GraphQL API, включая улучшение способов подключения и взаимодействия с базами данных. Это снимает часть трудностей, связанных с написанием эффективных SQL-запросов.

В этой статье я покажу, как автоматически генерировать GraphQL API для вашей базы данных PostgreSQL и соединять данные декларативно, используя только GraphQL SDL. Если вы думали, что SQL уже был декларативным, думаю, вы оцените пользовательские директивы, которые есть в StepZen.

Создание GraphQL API из PostgreSQL

С помощью StepZen вы можете создать GraphQL API для любого источника данных, включая базы данных PostgreSQL. Если у вас уже есть база данных, вы можете импортировать ее схему в GraphQL API с помощью StepZen CLI. В противном случае вы можете использовать учетные данные из нашей демонстрационной базы данных на этой странице.

Подключитесь к базе данных с помощью psql из терминала/командной строки:

`psql -h postgresql.introspection.stepzen.net -d introspection -U testUserIntrospection`

Войдите в полноэкранный режим Выйти из полноэкранного режима

С паролем: HurricaneStartingSample1934.

После подключения вы сможете общаться с базой данных PostgreSQL. Используя команду dt, вы сможете просмотреть таблицы, которые есть в этой базе данных:

              ```
{% endraw %}
bash
List of relations
 Schema |      Name       | Type  |  Owner
--------+-----------------+-------+----------
 public | address         | table | postgres
 public | customer        | table | postgres
 public | customeraddress | table | postgres
 public | lineitem        | table | postgres
 public | order           | table | postgres
 public | product         | table | postgres
(6 rows)
{% raw %}

Войти в полноэкранный режим Выход из полноэкранного режима



As you can see in the result above, the database has six tables. We can query every table using SQL or get the data from this database with StepZen. Therefore we first need to generate a GraphQL API based on introspection of this database.

To generate a GraphQL API for this database, you need to install the StepZen CLI:



```bash
`npm i -g stepzen`

Войти в полноэкранный режим Выйти из полноэкранного режима

И выполнить команду:

`stepzen import postgresql`

Enter fullscreen mode Выйти из полноэкранного режима

CLI запросит учетные данные базы данных, которые вы можете скопировать из нашего примера начала работы, если у вас нет собственной базы данных PostgreSQL.

Примечание: CLI спросит, хотите ли вы связать типы с помощью @materializer. Выберите здесь «да».

Когда CLI закончит импортировать вашу схему PostgreSQL и создаст схему GraphQL, вы найдете новый файл postgresql/index.graphql. Этот файл содержит схему GraphQL для вашей базы данных и содержит объявления типов и набор операций. Чтобы развернуть схему GraphQL и создать API, вы можете выполнить команду stepzen start.

После этого StepZen развернет схему GraphQL и вернет вашу конечную точку прямо в терминале.

Продолжим в следующем разделе, где мы рассмотрим объединение данных из разных таблиц в схеме GraphQL.

Соединение таблиц с помощью @materializer

Схема GraphQL, созданная для базы данных PostgreSQL путем выполнения команды stepzen import postgresql, уже содержит запрос, который объединяет данные из разных таблиц базы данных. Допустим, вы хотите получить адрес клиента, который хранится в отдельной таблице; вам нужно объединить разные таблицы в SQL.

При использовании SQL для получения клиента с идентификатором 1 и адреса этого клиента необходимо написать SQL-запрос следующим образом:

`SELECT T."city", T."countryregion", T."id", T."postalcode", T."stateprovince", T."street" FROM "public"."address" T, "public"."customeraddress" V WHERE V."customerid" = 1 AND V."addressid" = T."id"`

Войти в полноэкранный режим Выйти из полноэкранного режима

Этот SQL-запрос объединяет данные из таблиц customer и customeraddress.

В StepZen вы можете использовать этот же «сырой» SQL-запрос для создания GraphQL-запроса для объединения этих таблиц. Но вы также можете объединить эти данные более декларативно, не составляя SQL-запрос. Например, при использовании GraphQL-запроса getCustomer, StepZen получает информацию из таблицы customers, как показано в директиве @dbquery ниже:

getCustomer(id: Int!): Customer
    @dbquery(
      type: "postgresql"
      schema: "public"
      table: "customer"
      configuration: "postgresql_config"
    )
Вход в полноэкранный режим Выход из полноэкранного режима

Но посмотрите на его тип ответа Customer. Обратите внимание, что запрашиваемые поля содержат информацию об адресе и заказах, сделанных этим клиентом. Тип ответа Customer содержит соединения с этими таблицами, используя пользовательскую директиву @materializer.

type Customer {
  addressList: [Address] @materializer(query: "getAddressUsingCustomeraddress")
  email: String!
  id: Int!
  name: String!
  orderList: [Order] @materializer(query: "getOrderUsingCustomerid")
}
Вход в полноэкранный режим Выход из полноэкранного режима

Когда вы делаете запрос getCustomer, StepZen сначала получает информацию из таблицы клиентов. Когда вы включаете поля addressList или orderList, он использует GraphQL-запросы, связанные в @materializer, чтобы получить данные для этих полей.

Следующий GraphQL-запрос получает данные для полей id и email из таблицы customer в базе данных PostgreSQL; и данные для addressList путем выполнения запроса getAddressUsingCustomerid. Значение поля Customer.id передается в этот запрос в качестве аргумента.

{
  getCustomer(id: "1") {
    id
    email
    addressList {
      street
      city
    }
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Запрос getAddressUsingCustomerid использует необработанный SQL-запрос для получения адреса на основе идентификатора клиента. Это просто, поскольку таблица customeraddress содержит поле customerid:

getAddressUsingCustomerid(id: Int!): [Address]
    @dbquery(
      type: "postgresql"
      query: """
      SELECT T."city", T."countryregion", T."id", T."postalcode", T."stateprovince", T."street"
        FROM "public"."address" T, "public"."customeraddress" V
        WHERE V."customerid" = $1
          AND V."addressid" = T."id"
      """
      configuration: "postgresql_config"
    )
Войти в полноэкранный режим Выход из полноэкранного режима

Помимо включения необработанного SQL-запроса или использования @materializer вы также можете использовать другую пользовательскую директиву (@sequence) для выполнения последовательности запросов и сбора результатов, как вы увидите в следующем разделе.

Сбор данных с помощью @sequence

Иногда данные, которые вы хотите объединить, находятся не непосредственно в другой таблице, а в таблице, связанной через таблицу, содержащую поля, которые вы хотите объединить. Предположим, вы хотите объединить таблицы order и product из базы данных, которую мы используем в этой статье; вы увидите, что ни в одной из таблиц нет ссылки на другую таблицу. Вместо этого база данных содержит таблицу lineitem, которая включает только поля orderid и productid.

Вы можете использовать эту таблицу для объединения данных в необработанном SQL-запросе, чтобы получить информацию о продукте для заказа. И связать ее с новым запросом GraphQL:

getProductsUsingOrderid(orderid: Int!): [Product]
  @dbquery(
    type: "postgresql"
    query: """
    SELECT T."id", T."title", T."description", T."image" FROM "public"."product" T, "public"."lineitem" V WHERE V."orderid" = $1 AND T."id" = V."productid"
    """
    configuration: "postgresql_config"
  )
Войти в полноэкранный режим Выйти из полноэкранного режима

Этот запрос можно связать с типом Order с помощью директивы @materializer. Таким образом, вы можете добавлять информацию о товаре в заказы. Но вы можете сделать то же самое без написания необработанного SQL для соединения таблиц lineitem и product.

Вместо этого вы можете использовать пользовательскую директиву @sequence. С помощью @sequence вы можете выполнять запросы пошагово и собирать результаты:

Войти в полноэкранный режим Выйти из полноэкранного режима

graphql
getProductsUsingOrderid(id: Int!): [Product].
@sequence(
шаги: [
{ query: «getLineitemUsingOrderid» }
{ запрос: «getProduct», аргументы: [{ { name: «id», field: «productid» }] }
]
)


Вход в полноэкранный режим Выход из полноэкранного режима

Приведенный выше запрос getLineitemUsingOrderid сначала выполняет запрос getLineitemUsingOrderid и получает идентификаторы товаров для заказа. Эти идентификаторы передаются в запрос getProduct для сбора информации о продукте. Таким образом, вы можете получить данные о продукте без написания SQL-запроса для объединения данных из разных таблиц базы данных.

С помощью этой последовательности можно сделать и многое другое. Допустим, вы хотите ограничить количество полей, возвращаемых новым запросом GraphQL. Тогда вы можете использовать collect GraphQL-запрос, чтобы собрать только те поля, которые вы хотите раскрыть:

collect(
    id: Int!
    title: String
  ): Product @connector(type: "echo")
getProductsUsingOrderid(id: Int!): [Product]
    @sequence(
      steps: [
        { query: "getLineitemUsingOrderid" }
        { query: "getProduct", arguments: [{ name: "id", field: "productid" }] }
        { query: "collect" }
      ]
    )
Войти в полноэкранный режим Выход из полноэкранного режима

Благодаря добавлению этого третьего шага, поля, раскрываемые таблицей product, теперь ограничены только id и title. Вы также можете использовать этот запрос collect для получения данных из любых других шагов в @sequence.

Заключение

В этой статье вы узнали, как объединять данные из разных таблиц PostgreSQL без необходимости писать необработанные SQL-запросы. Вместо этого вы можете использовать пользовательские директивы StepZen для декларативного построения и объединения GraphQL API. Мы будем рады услышать, какой проект вы начнете создавать с помощью StepZen и PostgreSQL. Присоединяйтесь к нашему Discord, чтобы быть в курсе новостей нашего сообщества.

Оцените статью
devanswers.ru
Добавить комментарий