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-cliCLI устанавливается как зависимость для разработки, поскольку мы используем его для создания оптимизированных артефактов времени выполнения, которые могут быть загружены непосредственно из Вашего приложения!
Создайте конфигурационный файл (под названием .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.
