The Graph — запросы к субграфу
Как отправлять GraphQL-запросы к The Graph subgraph, получать DeFi-данные и работать с variables, pagination и errors в TypeScript.
Subgraph индексирует события и состояние
The Graph превращает blockchain-данные в GraphQL API.
Вместо ручного чтения logs, декодирования events и хранения индексов вы запрашиваете готовые сущности: pools, swaps, positions, tokens, volume, TVL и другие данные, которые subgraph уже собрал из сети.
Запрашиваем Uniswap V3 pools
Официальный Gateway The Graph требует API key. Храните ключ на сервере или в локальной переменной окружения, не в frontend bundle.
Создайте проект:
mkdir the-graph-example
cd the-graph-example
npm init -y
npm pkg set type="module"
npm install tsx
mkdir srcСоздайте файл src/query-uniswap-v3.ts:
const API_KEY = process.env.THE_GRAPH_API_KEY;
if (!API_KEY) {
throw new Error("Set THE_GRAPH_API_KEY environment variable");
}
const UNISWAP_V3_SUBGRAPH_ID = "HUZDsRpEVP2AvzDCyzDHtdc64dyDxx8FQjzsmqSg4H3B";
const endpoint = `https://gateway.thegraph.com/api/${API_KEY}/subgraphs/id/${UNISWAP_V3_SUBGRAPH_ID}`;
type GraphResponse<T> = {
data?: T;
errors?: Array<{
message: string;
}>;
};
type PoolsQuery = {
pools: Array<{
id: string;
feeTier: string;
liquidity: string;
totalValueLockedUSD: string;
volumeUSD: string;
token0: {
symbol: string;
};
token1: {
symbol: string;
};
}>;
};
const query = /* GraphQL */ `
query TopPools($first: Int!, $skip: Int!, $minTvl: BigDecimal!) {
pools(
first: $first
skip: $skip
orderBy: totalValueLockedUSD
orderDirection: desc
where: { totalValueLockedUSD_gt: $minTvl }
) {
id
feeTier
liquidity
totalValueLockedUSD
volumeUSD
token0 {
symbol
}
token1 {
symbol
}
}
}
`;
const response = await fetch(endpoint, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
query,
variables: {
first: 5,
skip: 0,
minTvl: "1000000",
},
}),
});
if (!response.ok) {
throw new Error(`Graph request failed: ${response.status} ${response.statusText}`);
}
const json = (await response.json()) as GraphResponse<PoolsQuery>;
if (json.errors?.length) {
throw new Error(json.errors.map((error) => error.message).join("\n"));
}
if (!json.data) {
throw new Error("Graph response does not contain data");
}
for (const pool of json.data.pools) {
console.log(`${pool.token0.symbol}/${pool.token1.symbol}`);
console.log(` pool: ${pool.id}`);
console.log(` fee: ${Number(pool.feeTier) / 10_000}%`);
console.log(` TVL: $${Number(pool.totalValueLockedUSD).toLocaleString("en-US")}`);
console.log(` volume: $${Number(pool.volumeUSD).toLocaleString("en-US")}`);
}Перед запуском сохраните реальный Gateway API key в переменную окружения THE_GRAPH_API_KEY.
Запустите:
npx tsx src/query-uniswap-v3.tsНа Windows PowerShell:
npx tsx src/query-uniswap-v3.tsСкрипт выведет примерно такой результат:
WETH/USDC
pool: 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640
fee: 0.05%
TVL: $421,902,410.91
volume: $927,124,193,520.44
WBTC/WETH
pool: 0xcbcdf9626bc03e24f779434178a73a0b4bad62ed
fee: 0.3%
TVL: $312,604,118.18
volume: $68,934,519,901.12Данные будут отличаться: subgraph постоянно индексирует новые blocks.
Как это работает
UNISWAP_V3_SUBGRAPH_ID — идентификатор subgraph в The Graph Gateway.
query TopPools(...) — именованный GraphQL-запрос. Имена помогают читать логи и ошибки.
$first, $skip, $minTvl — variables. Так вы не склеиваете GraphQL строку руками и можете безопасно менять параметры.
first — сколько сущностей вернуть за один запрос.
skip — offset для pagination. Для больших списков лучше использовать cursor-подход через id_gt, но skip удобен для простого старта.
where — фильтр на стороне subgraph. В примере мы берём только pools с TVL больше 1,000,000.
orderBy и orderDirection — сортировка. Здесь pools идут по totalValueLockedUSD от большего к меньшему.
json.errors — GraphQL может вернуть HTTP 200, но при этом содержать ошибки выполнения запроса. Проверяйте errors, а не только response.ok.
Subgraph не заменяет RPC
Subgraph хорош для данных, которые неудобно получать через один eth_call:
- история swap и mint events
- списки pools и positions
- агрегированные volume, TVL и fees
- фильтрация и сортировка по indexed fields
Но subgraph не является финальным источником текущего State. Он может отставать на несколько blocks, переиндексироваться или иметь schema-специфичные правила расчёта.
Если нужно принять финансовое решение в транзакции, проверяйте критичные данные on-chain: через контракт, oracle feed или собственную validation-логику.
Частые ошибки
Держать API key в frontend → Gateway key должен жить на сервере, в backend route или в локальном dev окружении.
Считать subgraph данные мгновенными → индексатору нужно время, чтобы обработать новые blocks. Для pending-state используйте RPC.
Игнорировать errors в GraphQL ответе → запрос может вернуть 200 OK и одновременно список ошибок.
Использовать skip для очень глубокой pagination → большие offsets становятся медленными. Для production используйте cursor по id_gt или другому indexed field.
Что дальше
- Multicall3 — батчинг вызовов — читать текущий on-chain State одним RPC-запросом
- Chainlink Price Feed — получать oracle-цену для критичной DeFi-логики
- Цена токена из Uniswap V3 через Quoter — получить spot-quote напрямую из Uniswap V3