10 минуты
Инструменты клиента The Graph
Этот репозиторий является домом для потребительских инструментов The Graph (как для браузерных, так и для NodeJS сред).
Предисловие
Инструменты, предоставленные в этом репозитории, предназначены для улучшения и расширения разработческого опыта (DX), а также для добавления дополнительного слоя, необходимого для децентрализованных приложений (dApps), чтобы реализовать распределенные приложения.
Разработчики, которые потребляют данные через GraphQL API от The Graph, часто нуждаются в периферийных инструментах для облегчения потребления данных, а также в инструментах, которые позволяют использовать несколько индексаторов одновременно.
Функции и цели
Эта библиотека предназначена для упрощения сетевого аспекта потребления данных для децентрализованных приложений (dApps). Инструменты, предоставленные в этом репозитории, предназначены для работы во время сборки, чтобы сделать выполнение более быстрым и производительным в момент выполнения.
Инструменты, предоставленные в этом репозитории, могут использоваться как самостоятельно, так и в сочетании с любым существующим GraphQL клиентом!
Статус | Функция | Примечания |
---|---|---|
✅ | Несколько индексаторов | основано на стратегиях выборки |
✅ | Стратегия выборки | timeout, retry, fallback, race, highestValue |
✅ | Валидации и оптимизации во время сборки | |
✅ | Композиция на стороне клиента | с улучшенным планировщиком выполнения (на основе GraphQL-Mesh) |
✅ | Кросс-чейн обработка субграфа | Использование схожих субграфов как единого источника |
✅ | Выполнение сырых данных (автономный режим) | напрямую, без GraphQL-клиента |
✅ | Местные (клиентские) мутации | |
✅ | Отслеживание автоматического блока | отслеживание номеров блоков как описано здесь |
✅ | Автоматическая пагинация | выполнение нескольких запросов в одном вызове для получения больше лимита индексатора |
✅ | Интеграция с @apollo/client | |
✅ | Интеграция с urql | |
✅ | Поддержка TypeScript | со встроенным GraphQL Codegen и TypedDocumentNode |
✅ | @live запросы | На основе опроса |
Вы можете найти расширенный архитектурный дизайн здесь
Начало работы
Вы можете подписаться на Episode 45 из graphql.wtf
, чтобы узнать больше о Graph Client:
Чтобы начать, убедитесь, что установили [The Graph Client CLI] в свой проект:
1yarn add -D @graphprotocol/client-cli2# или, с NPM:3npm install --save-dev @graphprotocol/client-cli
CLI устанавливается как зависимость для разработки, поскольку мы используем его для создания оптимизированных артефактов времени выполнения, которые могут быть загружены непосредственно из Вашего приложения!
Создайте конфигурационный файл (под названием .graphclientrc.yml
) и укажите Ваши GraphQL конечные точки, предоставленные The Graph, например:
1# .graphclientrc.yml2sources:3 - name: uniswapv24 handler:5 graphql:6 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
Теперь создайте артефакт времени выполнения, запустив The Graph Client CLI:
1graphclient build
Примечание: Вам нужно выполнить это с префиксом yarn
, или добавить это как скрипт в свой package.json
.
Это должно создать готовую к использованию автономную функцию execute
, которую Вы сможете использовать для выполнения операций GraphQL в своем приложении. Вы должны получить вывод, похожий на следующий:
1GraphClient: Очистка существующих артефактов 2GraphClient: Чтение конфигурации 3🕸️: Генерация унифицированной схемы 4🕸️: Генерация артефактов 5🕸️: Генерация индекса в TypeScript 6🕸️: Запись index.ts для ESM на диск 7🕸️: Очистка 8🕸️: Готово! => .graphclient
Теперь артефакт .graphclient
для Вас сгенерирован, и Вы можете импортировать его напрямую в свой код и выполнять запросы:
1import { execute } from '../.graphclient'23const myQuery = gql`4 query pairs {5 pair(id: "0x00004ee988665cdda9a1080d5792cecd16dc1220") {6 id7 token0 {8 id9 symbol10 name11 }12 token1 {13 id14 symbol15 name16 }17 }18 }19`2021async function main() {22 const result = await execute(myQuery, {})23 console.log(result)24}2526main()
Использование Vanilla JavaScript вместо TypeScript
По умолчанию, GraphClient CLI генерирует артефакты клиента в виде файлов TypeScript, но Вы можете настроить CLI для генерации файлов JavaScript и JSON вместе с дополнительными файлами определений TypeScript, используя --fileType js
или --fileType json
.
Флаг js
генерирует все файлы как JavaScript файлы с синтаксисом ESM, а флаг json
генерирует исходные артефакты как JSON файлы, при этом файл точки входа будет на старом синтаксисе CommonJS, поскольку только CommonJS поддерживает JSON файлы как модули.
Если Вы специально не используете CommonJS (require
), мы рекомендуем использовать флаг js
.
graphclient --fileType js
- Пример использования JavaScript в синтаксисе CommonJS с JSON файлами
- Пример использования JavaScript в синтаксисе ESM
Инструменты разработки The Graph Client
The Graph Client CLI включает встроенный GraphiQL, который позволяет Вам экспериментировать с запросами в реальном времени.
GraphQL-схема, обслуживаемая в этой среде, представляет собой итоговую схему, основанную на всех составленных субграфах и примененных преобразованиях.
Чтобы запустить DevTool GraphiQL, выполните следующую команду:
1graphclient serve-dev
А затем откройте http://localhost:4000/, чтобы использовать GraphiQL. Теперь Вы можете экспериментировать со своей GraphQL-схемой на стороне клиента локально! 🥳
Примеры
Вы также можете обратиться к каталогу с примерами в этом репозитории для более продвинутых примеров и примеров интеграции:
- Пример TypeScript и React с использованием
execute
и встроенного GraphQL-Codegen - Автономный режим TS/JS NodeJS
- Клиентская композиция GraphQL
- Интеграция с Urql и React
- Интеграция с NextJS и TypeScript
- Интеграция с Apollo-Client и React
- Интеграция с React-Query
- Кросс-чейн слияние (тот же субграф, разные чейны)
- Настройка выполнения с помощью трансформаций (автоматическая пагинация и автоматический отслеживание блоков)
Продвинутые примеры/функции
Настройка сетевых вызовов
Вы можете настроить выполнение сетевых запросов (например, для добавления заголовков аутентификации), используя operationHeaders
:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 operationHeaders:7 Authorization: Bearer MY_TOKEN
Вы также можете использовать переменные времени выполнения, если хотите, и указать их декларативным способом:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 operationHeaders:7 Authorization: Bearer {context.config.apiToken}
Затем Вы можете указать следующее, когда выполняете операции:
1execute(myQuery, myVariables, {2 config: {3 apiToken: 'MY_TOKEN',4 },5})
Полную документацию по обработчику graphql
можно найти здесь.
Интерполяция переменных среды
Если Вы хотите использовать переменные среды в конфигурационном файле своего Graph Client, Вы можете использовать интерполяцию с помощью помощника env
:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 operationHeaders:7 Authorization: Bearer {env.MY_API_TOKEN} # время выполнения
Затем убедитесь, что MY_API_TOKEN
определён, когда Вы выполняете process.env
во время выполнения программы.
Вы также можете указать переменные среды, которые будут заполняться во время сборки (при запуске graphclient build
), используя непосредственно имя переменной средв:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 operationHeaders:7 Authorization: Bearer ${MY_API_TOKEN} # время разработки
Полную документацию по обработчику graphql
можно найти здесь.
Стратегии выборки данных и работа с несколькими Graph-индексаторами
Это обычная практика — использовать несколько индексаторов в децентрализованных приложениях (dApps), поэтому для достижения наилучшего опыта работы с The Graph Вы можете указать несколько стратегий fetch
, чтобы сделать процесс более плавным и простым.
Все стратегииfetch
можно комбинировать для создания идеального потока выполнения.
`retry`
Механизм retry
позволяет указать количество попыток повторного запроса для одной GraphQL конечной точки/источника.
Механизм повторных попыток будет выполняться в обоих случаях: при ошибке сети или при ошибке выполнения (проблемы с индексированием/недоступность индексатора).
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 retry: 2 # укажите здесь, если у вас нестабильный/подверженный ошибкам индексатор
`timeout`
Механизм timeout
позволяет задать timeout
для указанной конечной точки GraphQL.
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 timeout: 5000 # 5 секунд
`fallback`
Механизм fallback
позволяет указать несколько конечных точек GraphQL для одного и того же источника.
Это полезно, если Вы хотите использовать более одного индексатора для одного и того же субграфа и переключаться на другой в случае ошибки или тайм-аута. Вы также можете использовать эту стратегию для использования кастомного индексатора, но в случае необходимости переключаться на The Graph Hosted Service.
1sources:2 - name: uniswapv23 handler:4 graphql:5 strategy: fallback6 sources:7 - endpoint: https://bad-uniswap-v2-api.com8 retry: 29 timeout: 500010 - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
`race`
Механизм race
позволяет указать несколько GraphQL-эндпоинтов для одного источника данных, выполняя их конкурентный опрос при каждом запросе.
Это полезно, если вы хотите использовать несколько индексаторов для одного субграфа и позволить им конкурировать за получение самого быстрого ответа от всех указанных индексаторов.
1sources:2 - name: uniswapv23 handler:4 graphql:5 strategy: race6 sources:7 - endpoint: https://bad-uniswap-v2-api.com8 - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
`highestValue`
Эта стратегия позволяет отправлять параллельные запросы к различным конечным точкам для одного и того же источника и выбирать наиболее актуальный ответ.
Это полезно, если Вы хотите выбрать наиболее синхронизированные данные для одного субграфа среди нескольких индексаторов/источников.
1sources:2 - name: uniswapv23 handler:4 graphql:5 strategy: highestValue6 strategyConfig:7 selectionSet: |8 {9 _meta {10 block {11 number12 }13 }14 }15 value: '_meta.block.number'16 sources:17 - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2-118 - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2-2
Отслеживание блоков
Graph Client может отслеживать номера блоков и выполнять следующие запросы, следуя этой схеме с использованием преобразования blockTracking
;
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 transforms:7 - blockTracking:8 # Вы можете отключить проверку схемы для более быстрого старта9 validateSchema: true10 # Игнорируйте поля, которые вы не хотите отслеживать11 ignoreFieldNames: [users, prices]12 # Исключите операции с указанными именами13 ignoreOperationNames: [NotFollowed]
Здесь Вы можете попробовать рабочий пример
Автоматическая пагинация
Для большинства субграфов количество записей, которые Вы можете извлечь, ограничено. В этом случае Вам нужно отправить несколько запросов с пагинацией.
1query {2 # Выдаст ошибку, если лимит равен 10003 users(first: 2000) {4 id5 name6 }7}
Таким образом, Вам нужно отправить следующие операции одну за другой:
1query {2 # Выдаст ошибку, если лимит равен 10003 users(first: 1000) {4 id5 name6 }7}
Затем после первого ответа:
1query {2 # Выдаст ошибку, если лимит равен 10003 users(first: 1000, skip: 1000) {4 id5 name6 }7}
После второго ответа Вам пришлось бы вручную объединять результаты. Однако Graph Client позволяет выполнить первый запрос, а затем в фоновом режиме обрабатывает все остальные.
Всё, что Вам нужно сделать, это:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 transforms:7 - autoPagination:8 # Вы можете отключить проверку схемы для более быстрого старта9 validateSchema: true
Здесь Вы можете попробовать рабочий пример
Композиция на стороне клиента
Graph Client имеет встроенную поддержку композиции GraphQL на стороне клиента (реализованную с помощью GraphQL-Tools Schema-Stitching).
Вы можете использовать эту функцию для создания единого слоя GraphQL из нескольких субграфов, развернутых на нескольких индексаторах.
💡 Совет: Вы можете комбинировать любые источники GraphQL, а не только субграфы!
Тривиальную композицию можно выполнить, добавив более одного источника GraphQL в Ваш файл .graphclientrc.yml
, вот пример:
1sources:2 - name: uniswapv23 handler:4 graphql:5 endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v26 - name: compoundv27 handler:8 graphql:9 endpoint: https://api.thegraph.com/subgraphs/name/graphprotocol/compound-v2
Пока нет конфликтов между объединёнными схемами, Вы можете их составлять, а затем выполнить один запрос ко всем субграфам:
1query myQuery {2 # этот запрос поступает от compound-v23 markets(first: 7) {4 borrowRate5 }6 # этот запрос поступает от uniswap-v27 pair(id: "0x00004ee988665cdda9a1080d5792cecd16dc1220") {8 id9 token0 {10 id11 }12 token1 {13 id14 }15 }16}
Вы также можете разрешать конфликты, переименовывать части схемы, добавлять пользовательские поля GraphQL и изменять всю фазу выполнения.
Для сложных сценариев использования композиций обратитесь к следующим ресурсам:
- Пример сложной композиции
- Преобразования схемы GraphQL-Mesh
- Документация по объединению схем с помощью GraphQL-Tools
Поддержка TypeScript
Если Ваш проект написан на TypeScript, Вы можете использовать возможности TypedDocumentNode
и получить полностью типизированный опыт работы с GraphQL-клиентом.
Автономный режим The GraphQL, а также популярные библиотеки GraphQL-клиентов, такие как Apollo-Client и urql, имеют встроенную поддержку TypedDocumentNode
!
CLI Graph Client поставляется с готовой конфигурацией для GraphQL Code Generator и может генерировать TypedDocumentNode
на основе Ваших GraphQL-операций.
Чтобы начать, определите Ваши GraphQL-операции в коде приложения и укажите пути к этим файлам в разделе documents
файла .graphclientrc.yml
:
1sources:2 - # ... Ваши Субграфы/источники GQL здесь34documents:5 - ./src/example-query.graphql
Вы также можете использовать выражения Glob или даже указывать файлы кода, и CLI автоматически найдет Ваши GraphQL-запросы:
1documents:2 - './src/**/*.graphql'3 - './src/**/*.{ts,tsx,js,jsx}'
Теперь снова выполните команду build
в GraphQL CLI, и CLI сгенерирует объект TypedDocumentNode
в .graphclient
для каждой найденной операции.
Обязательно давайте имена Вашим GraphQL-операциям, иначе они будут проигнорированы!
Например, для запроса с именем query ExampleQuery
будет сгенерирован соответствующий ExampleQueryDocument
в .graphclient
. Теперь вы можете импортировать его и использовать для GraphQL-запросов, получая полностью типизированный опыт без необходимости вручную писать или указывать TypeScript:
1import { ExampleQueryDocument, execute } from '../.graphclient'23async function main() {4 // переменная "result" полностью типизирована и представляет точную структуру полей, которые вы выбрали в вашем запросе.5 const result = await execute(ExampleQueryDocument, {})6 console.log(result)7}
Вы можете найти пример проекта на TypeScript здесь.
Мутации на стороне клиента
Из-за особенностей настройки Graph-Client, возможно добавление схемы на стороне клиента, которую затем можно использовать для выполнения произвольного кода.
Это полезно, потому что Вы можете внедрить пользовательский код в часть своей схемы GraphQL и использовать его как единую схему приложения, что облегчает отслеживание и разработку.
Этот документ объясняет, как добавить пользовательские мутации, но на самом деле Вы можете добавить любую операцию GraphQL (запросы/мутации/подписки). Для получения дополнительной информации о данной функции, см. статью Расширение единой схемы.
Чтобы начать, определите раздел additionalTypeDefs
в Вашем конфигурационном файле:
1additionalTypeDefs: |2 # Мы должны определить отсутствующий тип `Mutation`3 extend schema {4 mutation: Mutation5 }67 type Mutation {8 doSomething(input: SomeCustomInput!): Boolean!9 }1011 input SomeCustomInput {12 field: String!13 }
Затем добавьте указатель на файл с пользовательскими GraphQL-ресолверами:
1additionalResolvers:2 - './resolvers'
Теперь создайте файл resolver.js
(или resolvers.ts
) в своем проекте и внедрите свою пользовательскую мутацию:
1module.exports = {2 Mutation: {3 async doSomething(root, args, context, info) {4 // Здесь Вы можете выполнить все, что хотите.5 // Например, использовать библиотеку `web3`, подключить кошелек и так далее.67 return true8 },9 },10}
Если Вы используете TypeScript, Вы также можете получить полностью безопасную типизацию подписей, сделав следующее:
1import { Resolvers } from './.graphclient'23// Теперь всё написано!4const resolvers: Resolvers = {5 Mutation: {6 async doSomething(root, args, context, info) {7 // Здесь Вы можете выполнить любые операции, которые хотите.8 // Например, использовать библиотеку `web3`, подключить кошелек и так далее.910 return true11 },12 },13}1415export default resolvers
Если Вам нужно внедрить переменные времени выполнения в Ваш context
выполнения GraphQL, вы можете использовать следующий сниппет:
1execute(2 MY_QUERY,3 {},4 {5 myHelper: {}, // это будет доступно в Вашем ресолвере мутации как `context.myHelper`6 },7)
Лицензия
Выпущена под лицензией MIT.