Query the Hosted Service

Prerequisites

Install Rust and Cargo, the Rust package manager

  1. Open your Terminal application then run the following command

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Instruction are valid for Linux, macOS, and Windows Subsystem for Linux

Overview

The Graph is a decentralized protocol that enables developers to query data from blockchains using GraphQL. In this lesson, we will use Rust to submit a GraphQL query to the graph-network-mainnet subgraph on The Graph’s Hosted Service. This hands-on lesson will provide you with the practical experience needed to interface with The Graph's ecosystem, and moreover, demonstrate the immense potential of leveraging Rust to interact with decentralized data networks. Let's dig in and uncover the incredible possibilities that await us in the realm of Rust, GraphQL, and The Graph's Hosted Service!

GraphQL query

To start, let's discuss the subgraph and GraphQL query we're submitting. The graph-network-mainnet subgraph extracts data from the protocol's smart contracts and makes it accessible via GraphQL queries. Indexers are the node operators of the protocol and allocate GRT tokens to subgraphs as part of their operations.

In this lesson, we're requesting data about indexers that is defined in the protocol's smart contracts. Our query is filtering results to the first 10 indexers who have allocated at least 1 GRT token to any subgraph in the protocol. Finally we're narrowing down the returned fields to id, defaultDisplayName, and stakedTokens.

Table 1. Subgraph field descriptions

FieldDescription

id

the Ethereum address of the Indexer

defaultDisplayName

the current default display name of the Indexer. Potentially null and used to filter queries

stakedTokens

current tokens staked in The Graph protocol

Figure 1. Subgraph query

query indexers {
  indexers(first:10, where: {allocatedTokens_gt: "0"}) {
    id
    defaultDisplayName
    stakedTokens
  }
}

Getting started with Rust

Coming soon

Create a new cargo project

  1. Open your Terminal application and change to your home directory

cd ~
  1. Create a new directory called rohr and change into it

mkdir -p rohr && cd rohr
  1. Create a new cargo project called indexer_subgraph_query

cargo new indexer_subgraph_query
  1. Change into the new indexer_subgraph_query directory

cd indexer_subgraph_query

Add dependencies to your project

  1. Open Cargo.toml with nano

nano Cargo.toml
  1. Add the following contents below the [dependencies] section of Cargo.toml

    • serde is a framework for serializing and deserializing Rust data structures

    • reqwest provides a higher-level HTTP client

    • tokio is a platform for writing asynchronous applications

serde = { version = "1.0.149", features = ["derive"] }
reqwest = { version = "0.11", features =  ["json"]}
tokio = { version = "1.23.0", features = ["full"] }
  1. Save your changes to Cargo.toml and exit nano

ctrl + x
y

Write Rust code

structs.rs

  1. Create a new file called structs.rs in the src directory

nano src/structs.rs
  1. Add the following use declarations to src/structs.rs

use std::string::String;
use serde::Deserialize;

A use declaration creates one or more local name bindings synonymous with some other path. Usually, a use declaration is utilized to shorten the path required to refer to a module item. These declarations may appear in modules and blocks, often at the top.

-The Rust Reference

  1. Next define a few struct statements in src/structs.rs

#[allow(non_snake_case)]
#[derive(Debug, Deserialize, PartialEq)]
pub struct Indexer {
    pub id: String,
    pub defaultDisplayName: Option<String>,
    pub stakedTokens: Option<String>,
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct IndexerData {
    pub indexers: Vec<Indexer>
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct IndexerResponse {
    pub data: IndexerData
}

Notice that we created three structs:

  • IndexerResponse: the highest-level struct in the hierarchy. It defines the response format from the POST request sent to The Graph's Hosted Service

  • IndexerData and Indexer: nested structs which define data returned in the IndexerResponse

Each Indexer in The Graph protocol would theoretically be assigned to an Indexer struct, with the IndexerData struct containing all Indexer structs (or a subset based on the query filter submitted, i.e. first 10).

Finally, IndexerData is the value of the IndexerResponse data field.

  1. Save your changes to src/structs.rs and exit nano

ctrl + x
y

Your file structs.rs will look something like this

use std::string::String;
use serde::Deserialize;

#[allow(non_snake_case)]
#[derive(Debug, Deserialize, PartialEq)]
pub struct Indexer {
    pub id: String,
    pub defaultDisplayName: Option<String>,
    pub stakedTokens: Option<String>,
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct IndexerData {
    pub indexers: Vec<Indexer>
}

#[derive(Debug, Deserialize, PartialEq)]
pub struct IndexerResponse {
    pub data: IndexerData
}

main.rs

  1. Open src/main.rs with nano

nano src/main.rs
  1. Delete all existing lines in the file

ctrl + shift + k
  1. Add the following user declarations to the top of src/main.rs

use std::collections::HashMap;
mod structs;
use crate::structs::*;

Run your program

From your terminal, use cargo to compile and run your Rust program

cargo run

Last updated