TL;DR

В этом руководстве вы узнаете, как создавать безопасные запросы GraphQL с использованием TypeScript, используя некоторые отличные инструменты, методы и решения.

вступление

Когда вы строите компанию, одержимую идеей улучшения жизни клиентских команд, вполне естественно тратить много времени на размышления о таких вещах, как фронтенд.

И это многое объясняет в моей команде.

Но мы решили, что вместо того, чтобы просто мечтать, мы попытаемся превратить наш случайный мыслительный процесс во что-то более продуктивное и полезное для таких людей, как вы. 😅

Недавно мы размышляли о том, как сильно фронтенд-разработчики любят GQL. Нам также пришло в голову, что они тоже любят TypeScript.

Итак… как здорово было бы, если бы мы могли их объединить?

Как оказалось, существует множество инструментов, которые могут помочь разработчикам создавать безопасные для типов запросы GraphQL с использованием TypeScript. В этой статье мы рассмотрим лучшие инструменты, методы и решения, которые могут помочь создать отличный опыт для разработчиков.

Livecycle — лучший способ для фронтенд-разработчиков сотрудничать и быстро проверять PR.

[Теперь немного краткой информации о том, кто мы такие, чтобы вы поняли, почему мы мечтаем о таких вещах.

Livecycle — это контекстный инструмент совместной работы для команд разработчиков интерфейса. Мы знаем, как болезненно пытаться заставить несколько заинтересованных сторон просмотреть изменения, прежде чем они будут опубликованы (нечеткие комментарии… переключение контекста… разные часовые пояса… слишком много встреч… нулевая координация… вы знаете, точно о чем я) говоря о 🙄), поэтому мы создали решение.

Наш SDK превращает любую среду предварительного просмотра PR в площадку для совместной работы, где команда может оставлять комментарии визуального обзора в контексте. Упрощая контекстную асинхронную проверку, мы экономим фронтенд-командам массу времени, денег и головной боли. (Если ваша команда создает продукт вместе — узнайте, чем может помочь Livecycle)]

А теперь вернемся к нашей обычной запланированной программе… Пристегнитесь.

Автозаполнение для написания запросов

Давайте начнем с изучения некоторых методов отображения типов автозаполнения при написании запросов. Автозаполнение — это удобная функция, которая позволяет разработчикам видеть доступные параметры схемы для полей, аргументов, типов и переменных при написании запросов. Он также должен работать с файлами .graphql и строками шаблонов ggl.

Рис. 1. Автозаполнение в действии

Редактор, который вы используете, определит процесс настройки автозаполнения в TypeScript, а также способ применения правильной конфигурации.

Например, предположим, что у вас есть конечная точка GraphQL в http://localhost:10008/graphql и вы хотите включить для нее автозаполнение. Вам нужно изучить схему и использовать ее для заполнения параметров автозаполнения.

Самоанализ схемы

Вам нужно включить редактор, чтобы он соответствовал схеме и отображал разрешенные поля и типы по мере того, как вы пишете. Это легко сделать, если вы используете Webstorm с плагином GraphQL. Просто нажмите на вкладку, чтобы настроить конечную точку, которую вы хотите изучить, и она сгенерирует файл graphql-config, который выглядит примерно так:

Рис. 2. Плагин Webstorm GraphQL

// ./graphql-config.json
{
  "name": "MyApp",
  "schemaPath": "schema.graphql",
  "extensions": {
    "endpoints": {
      "Default GraphQL Endpoint": {
        "url": "http://localhost:10008/graphql",
        "headers": {
          "user-agent": "JS GraphQL"
        },
        "introspect": true
      }
    }
  }
}

После завершения этого шага вы сможете использовать автозаполнение при написании запросов ggl и .graphql.

Вы также можете сделать это с помощью VSCode с плагином VSCode GraphQL. Вы можете сначала загрузить файл схемы:

npm i get-graphql-schema -g get-graphql-schema http://localhost:10008/graphql > schema.graphql

Затем, если вы используете предыдущий файл graphql-config.json, там тоже все должно работать как положено.

Автоматическое создание типов из файлов Gql/Graphql

Если у вас включена функция автозаполнения, вы должны использовать TypeScript для возврата допустимых типов для данных результатов при написании файлов ggl.

В этом случае вам необходимо сгенерировать типы TypeScript из схемы GraphQL, чтобы вы могли использовать TypeScript Language Server для автозаполнения полей в коде.

Во-первых, вы используете инструмент под названием @graphql-codegen для выполнения схемы — › генерации типов TypeScript. Установите необходимые зависимости следующим образом:

npm i @graphql-codegen/introspection \ @graphql-codegen/TypeScript @graphql-codegen/TypeScript-operations \ @graphql-codegen/cli --save-dev

Затем создайте файл codegen.yml, содержащий конфигурацию генерации кода:

# ./codegen.yml
overwrite: true
schema: "http://localhost:10008/graphql"
documents: "pages/\*\*/\*.graphql"
generates:
  types/generated.d.ts:
    plugins:
      - typescript
      - typescript-operations
      - introspection

Просто добавьте в этот файл расположение конечной точки схемы, документы для сканирования на наличие запросов и расположение сгенерированных файлов.

Например, у нас есть файл posts.graphql со следующим содержимым:

# ./posts.graphql
query GetPostList {
  posts {
    nodes {
      excerpt
      id
      databaseId
      title
      slug
    }
  }
}

Затем мы добавляем эту задачу в package.json и запускаем ее:

// package.json

"scripts": {

/*
...
*/

"generate": "graphql-codegen --config codegen.yml",

}
npm run generate

Это создаст окружающие типы в types/generated.d.ts. Теперь мы можем использовать их в запросах:

import postsQuery from "./posts.graphql"
import { GetPostListQuery } from "../types/generated"

const [response] = useQuery<GetPostListQuery>({ query: postsQuery })

Примечание. Мы можем загружать файлы .graphql с помощью операторов импорта со следующим правилом веб-пакета:

{
      test: /\.(graphql|gql)$/,
      exclude: /node_modules/,
      loader: 'graphql-tag/loader',
 }

Теперь данные ответа будут правильно проверяться при доступе к соответствующим полям, даже если вы ничего не вводите:

Рис. 3. Использование сгенерированных типов в TypeScript

Вы также можете просмотреть файлы .graphql и автоматически сгенерировать соответствующие типы, не возвращаясь и не запуская те же команды снова, добавив флаг -w. Таким образом, типы всегда будут синхронизированы при их обновлении:

//package.json

"scripts": {

//…

"generate": "graphql-codegen --config codegen.yml -w,

}

Улучшенный вывод типа с использованием typed-document-node

Проект GraphQL Code Generator предлагает множество плагинов, которые позволяют улучшить процесс разработки с помощью Typescript и GraphQL. Одним из таких плагинов является typed-document-node, который позволяет разработчикам избежать импорта файла .graphql и вместо этого использовать сгенерированный тип Typescript для результата запроса. Когда вы вводите результат только что запрошенной операции, вы получаете автоматический вывод типа, автозаполнение и проверку типов.

Для его использования сначала необходимо установить сам плагин:

npm i @graphql-typed-document-node/core \ @graphql-codegen/typed-document-node --save-dev

Затем включите его в файл codegen.yml:

# ./codegen.yml

overwrite: true
schema: "http://localhost:10008/graphql"
documents: "pages/\*\*/*.graphql"
generates:
  types/generated.d.ts:
    plugins:
    - typescript
    - typescript-operations
    - typed-document-node
    - introspection

Теперь снова запустите команду generate, чтобы создать новый TypedDocumentNode, который расширяет интерфейс DocumentNode. Это обновит файл types/generated.d.ts, который теперь включает следующие типы:

export type GetPostListQueryVariables = Exact<{ [key: string]: never; }>;
export type GetPostListQuery = …
export const GetPostListDocument = …

Теперь вместо предоставления параметра общего типа в запросе вы просто используете константу GetPostListDocument и удаляете импорт .graphql, что позволяет Typescript делать выводы о типах результирующих данных. Таким образом, мы можем изменить index.tsx следующим образом:

index.tsx

import { GetPostListDocument } from "../types/generated.d";
…
const result = useQuery(GetPostListDocument);

Вы можете запросить данные ответа, которые будут выводить типы для вас по мере ввода:

В целом этот плагин значительно улучшает опыт разработки при работе с запросами GraphQL.

Библиотеки выборки данных для Интернета и Node/Deno

Наконец, давайте рассмотрим лучшие библиотеки выборки данных для Web и Node/Deno. Есть три основных претендента:

Graphql-запрос

Это минималистичный клиент GraphQL, и это самый простой способ вызвать конечную точку, помимо прямого вызова с помощью fetch. Вы можете использовать его для выполнения прямых запросов к конечной точке GraphQL без каких-либо дополнительных функций, таких как стратегии кэширования или повторная выборка. Однако это не мешает вам использовать функции автозаполнения и типизированных запросов:

npm i graphql-request --save
// ./getPosts.tsx

import { gql } from "@apollo/client"
import { request } from "graphql-request"
import { GetPostListQuery } from "../types/generated"

export const getPosts = async () => {
  const data = await request<GetPostListQuery>(
    "http://localhost:10003",
    gql`
        query GetPostList {
            posts {
                nodes {
                    excerpt
                    id
                    databaseId
                    title
                    slug
                }
            }
        }
    `
  )

  return data?.posts?.nodes?.slice() ?? []
}

Например, вы можете использовать функцию запроса для выполнения прямого запроса к конечной точке GraphQL, а затем передать сгенерированный тип GetPostListQuery. Пока вы вводите запрос, вы увидите возможность автозаполнения, а также увидите, что данные ответа введены правильно. В качестве альтернативы, если вы не хотите каждый раз передавать конечную точку, вы можете вместо этого использовать класс GraphQLClient:

import { GraphQLClient } from "graphql-request"
import { GetPostListQuery } from "../types/generated"

const client = new GraphQLClient("http://localhost:10003")

export const getPosts = async () => {
  const data = await client.request
}

Клиент Аполлона

Это один из старейших и наиболее полнофункциональных клиентов. Он предлагает множество полезных возможностей производственного уровня, таких как стратегии кэширования, повторная выборка и интегрированные состояния.

Начать работу с Apollo Client относительно просто. Вам просто нужно настроить клиент и передать его приложению:

npm install @apollo/client graphql
// client.ts

import { ApolloClient, InMemoryCache } from "@apollo/client"

const client = new ApolloClient({
  uri: "http://localhost:10003/graphql",
  cache: new InMemoryCache(),
  connectToDevTools: true,
})

Вы хотите указать uri для подключения, используемый механизм кэширования и флаг для подключения к плагину инструментов разработчика. Затем предоставьте клиенту параметры запроса или мутации для выполнения запросов:

// ./getPosts.tsx

import client from "./client"
import { gql } from "@apollo/client"
import { GetPostListQuery } from "../types/generated"

export const getPosts = async () => {
  const { data } = await client.query<GetPostListQuery>({
    query: gql`
          query GetPostList {
              posts {
                  nodes {
                      excerpt
                      id
                      databaseId
                      title
                      slug
                  }
              }
          }
      `,
  })

  return data?.posts?.nodes?.slice() ?? []
}

Если вы используете React, мы рекомендуем подключить клиент к соответствующему провайдеру следующим образом:

// ./index.tsx

import { ApolloProvider } from "@apollo/client"
import client from "./client"

const App = () => (
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>
)

В целом, Apollo — это стабильный и многофункциональный клиент, который удовлетворит все ваши потребности. Это хороший выбор.

Уркл

Это более современный клиент от Formidable Labs. По умолчанию он очень настраиваемый и очень гибкий. Urql использует концепцию обменов, которые похожи на функции промежуточного программного обеспечения, расширяющие его функциональность. Он также поставляется с собственным расширением инструменты разработчика и предлагает множество дополнений. Вы начинаете процесс установки (который очень похож на описанный выше) следующим образом:

npm install --save @urql/core graphql
// ./client.ts

import { createClient, defaultExchanges } from "urql"
import { devtoolsExchange } from "@urql/devtools"

const client = createClient({
  url: "http://localhost:10003/graphql",

  exchanges: [devtoolsExchange, ...defaultExchanges],
})

запросы.gql

# ./graphql/queries.gql

query GetPostList {
  posts {
    nodes {
      excerpt
      id
      databaseId
      title
      slug
    }
  }
}

Вам нужно указать URL-адрес для подключения и список бирж для использования. Затем вы можете выполнять запросы, вызывая метод запроса у клиента, а затем преобразовывая его в обещание с помощью метода toPromise:

// ./index.ts

import client from "./client"
import { GetPostDocument } from "../types/generated"

async function getPosts() {
  const { data } = await client.query(GetPostDocument).toPromise()
  return data
}

В целом, Urql может быть хорошей альтернативой Apollo, поскольку он обладает высокой расширяемостью и обеспечивает хороший опыт разработки.

Кроме того, если мы используем приложение React, мы можем установить плагин @graphql-codegen/typescript-urql и генерировать компоненты более высокого порядка или хуки для простого использования данных с нашего сервера GraphQL.

Итог и следующие шаги с GraphQL и TypeScript

В конце концов, мы просто хотим сделать разработчиков счастливыми. Поэтому мы искренне надеемся, что вам понравилось читать эту статью.

Для дальнейшего чтения вы можете изучить другие инструменты в экосистеме GraphQL, просмотрев список awesome-graphql.

Проверить нас!

Если вы нашли эту статью полезной, то вы, вероятно, относитесь к тому типу людей, которые оценят ценность, которую Livecycle может принести фронтенд-командам.

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