• Stars
    star
    172
  • Rank 221,201 (Top 5 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 3 years ago
  • Updated about 1 month ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The Graph library for building GraphQL-based dapps in a decentralized way.

The Graph Client Tools

This repo is the home for The Graph consumer-side tools (for both browser and NodeJS environments).

Background

The tools provided in this repo are intended to enrich and extend the DX, and add the additional layer required for dApps in order to implement distributed applications.

Developers who consume data from The Graph GraphQL API often need peripherials for making data consumption easier, and also tools that allow using multiple indexers at the same time.

Features and Goals

This library is intended to simplify the network aspect of data consumption for dApps. The tools provided within this repository are intended to run at build time, in order to make execution faster and performant at runtime.

The tools provided in this repo can be used as standalone, but you can also use it with any existing GraphQL Client!

Status Feature Notes
βœ… Multiple indexers based on fetch strategies
βœ… Fetch Strategies timeout, retry, fallback, race, highestValue
βœ… Build time validations & optimizations
βœ… Client-Side Composition with improved execution planner (based on GraphQL-Mesh)
βœ… Cross-chain Subgraph Handling Use similar subgraphs as a single source
βœ… Raw Execution (standalone mode) without a wrapping GraphQL client
βœ… Local (client-side) Mutations
βœ… Automatic Block Tracking tracking block numbers as described here
βœ… Automatic Pagination doing multiple requests in a single call to fetch more than the indexer limit
βœ… Integration with @apollo/client
βœ… Integration with urql
βœ… TypeScript support with built-in GraphQL Codegen and TypedDocumentNode
βœ… @live queries Based on polling

You can find an extended architecture design here

Getting Started

You can follow Episode 45 of graphql.wtf to learn more about Graph Client:

GraphQL.wtf Episode 45

To get started, make sure to install [The Graph Client CLI] in your project:

yarn add -D @graphprotocol/client-cli
# or, with NPM:
npm install --save-dev @graphprotocol/client-cli

The CLI is installed as dev dependency since we are using it to produce optimized runtime artifacts that can be loaded directly from your app!

Create a configuration file (called .graphclientrc.yml) and point to your GraphQL endpoints provided by The Graph, for example:

# .graphclientrc.yml
sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2

Now, create a runtime artifact by running The Graph Client CLI:

graphclient build

Note: you need to run this with yarn prefix, or add that as a script in your package.json.

This should produce a ready-to-use standalone execute function, that you can use for running your application GraphQL operations, you should have an output similar to the following:

GraphClient: Cleaning existing artifacts
GraphClient: Reading the configuration
πŸ•ΈοΈ: Generating the unified schema
πŸ•ΈοΈ: Generating artifacts
πŸ•ΈοΈ: Generating index file in TypeScript
πŸ•ΈοΈ: Writing index.ts for ESM to the disk.
πŸ•ΈοΈ: Cleanup
πŸ•ΈοΈ: Done! => .graphclient

Now, the .graphclient artifact is generated for you, and you can import it directly from your code, and run your queries:

import { execute } from '../.graphclient'

const myQuery = gql`
  query pairs {
    pair(id: "0x00004ee988665cdda9a1080d5792cecd16dc1220") {
      id
      token0 {
        id
        symbol
        name
      }
      token1 {
        id
        symbol
        name
      }
    }
  }
`

async function main() {
  const result = await execute(myQuery, {})
  console.log(result)
}

main()

Using Vanilla JavaScript Instead of TypeScript

GraphClient CLI generates the client artifacts as TypeScript files by default, but you can configure CLI to generate JavaScript and JSON files together with additional TypeScript definition files by using --fileType js or --fileType json.

js flag generates all files as JavaScript files with ESM Syntax and json flag generates source artifacts as JSON files while entrypoint JavaScript file with old CommonJS syntax because only CommonJS supports JSON files as modules.

Unless you use CommonJS(require) specifically, we'd recommend you to use js flag.

graphclient --fileType js

The Graph Client DevTools

The Graph Client CLI comes with a built-in GraphiQL, so you can experiment with queries in real-time.

The GraphQL schema served in that environment, is the eventual schema based on all composed Subgraphs and transformations you applied.

To start the DevTool GraphiQL, run the following command:

graphclient serve-dev

And open http://localhost:4000/ to use GraphiQL. You can now experiment with your Graph client-side GraphQL schema locally! πŸ₯³

Examples

You can also refer to examples directory in this repo, for more advanced examples and integration examples:

Advanced Examples/Features

Customize Network Calls

You can customize the network execution (for example, to add authentication headers) by using operationHeaders:

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
        operationHeaders:
          Authorization: Bearer MY_TOKEN

You can also use runtime variables if you wish, and specify it in a declarative way:

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
        operationHeaders:
          Authorization: Bearer {context.config.apiToken}

Then, you can specify that when you execute operations:

execute(myQuery, myVariables, {
  config: {
    apiToken: 'MY_TOKEN'
  }
})

You can find the complete documentation for the graphql handler here.

Environment Variables Interpolation

If you wish to use environment variables in your Graph Client configuration file, you can use interpolation with env helper:

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
        operationHeaders:
          Authorization: Bearer {env.MY_API_TOKEN} # runtime

Then, make sure to have MY_API_TOKEN defined when you run process.env at runtime.

You can also specify environment variables to be filled at build time (during graphclient build run) by using the env-var name directly:

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
        operationHeaders:
          Authorization: Bearer ${MY_API_TOKEN} # build time

You can find the complete documentation for the graphql handler here.

Fetch Strategies and Multiple Graph Indexers

It's a common practice to use more than one indexer in dApps, so to achieve the ideal experience with The Graph, you can specify several fetch strategies in order to make it more smooth and simple.

All fetch strategies can be combined to create the ultimate execution flow.

`retry`

The retry mechanism allow you to specify the retry attempts for a single GraphQL endpoint/source.

The retry flow will execute in both conditions: a netword error, or due to a runtime error (indexing issue/inavailability of the indexer).

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
        retry: 2 # specify here, if you have an unstable/error prone indexer
`timeout`

The timeout mechanism allow you to specify the timeout for a given GraphQL endpoint.

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
        timeout: 5000 # 5 seconds
`fallback`

The fallback mechanism allow you to specify use more than one GraphQL endpoint, for the same source.

This is useful if you want to use more than one indexer for the same Subgraph, and fallback when an error/timeout happens. You can also use this strategy in order to use a custom indexer, but allow it to fallback to The Graph Hosted Service.

sources:
  - name: uniswapv2
    handler:
      graphql:
        strategy: fallback
        sources:
          - endpoint: https://bad-uniswap-v2-api.com
            retry: 2
            timeout: 5000
          - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
`race`

The race mechanism allow you to specify use more than one GraphQL endpoint, for the same source, and race on every execution.

This is useful if you want to use more than one indexer for the same Subgraph, and allow both sources to race and get the fastest response from all specified indexers.

sources:
  - name: uniswapv2
    handler:
      graphql:
        strategy: race
        sources:
          - endpoint: https://bad-uniswap-v2-api.com
          - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
`highestValue`

This strategy allows you to send parallel requests to different endpoints for the same source and choose the most updated.

This is useful if you want to choose most synced data for the same Subgraph over different indexers/sources.

sources:
  - name: uniswapv2
    handler:
      graphql:
        strategy: highestValue
        strategyConfig:
          selectionSet: |
            {
              _meta {
                block {
                  number
                }
              }
            }
          value: '_meta.block.number'
        sources:
          - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2-1
          - endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2-2
graph LR;
    subgraph most-synced
    req(Outgoing Query)-->sA[Subgraph A];
    sA-->d{MostSyncedStrategy};
    d-->s1[Source 1];
    d-->s2[Source 2];
    s1-->synced["process"]
    s2-->synced
    synced-->|"max(_meta.block_number)"|d
    end

Block Tracking

The Graph Client can track block numbers and do the following queries by following this pattern with blockTracking transform;

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
    transforms:
      - blockTracking:
          # You might want to disable schema validation for faster startup
          validateSchema: true
          # Ignore the fields that you don't want to be tracked
          ignoreFieldNames: [users, prices]
          # Exclude the operation with the following names
          ignoreOperationNames: [NotFollowed]

You can try a working example here

Automatic Pagination

With most subgraphs, the number of records you can fetch is limited. In this case, you have to send multiple requests with pagination.

query {
  # Will throw an error if the limit is 1000
  users(first: 2000) {
    id
    name
  }
}

So you have to send the following operations one after the other:

query {
  # Will throw an error if the limit is 1000
  users(first: 1000) {
    id
    name
  }
}

Then after the first response:

query {
  # Will throw an error if the limit is 1000
  users(first: 1000, skip: 1000) {
    id
    name
  }
}

After the second response, you have to merge the results manually. But instead The Graph Client allows you to do the first one and automatically does those multiple requests for you under the hood.

All you have to do is:

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
    transforms:
      - autoPagination:
          # You might want to disable schema validation for faster startup
          validateSchema: true

You can try a working example here

Client-side Composition

The Graph Client has built-in support for client-side GraphQL Composition (powered by GraphQL-Tools Schema-Stitching).

You can leverage this feature in order to create a single GraphQL layer from multiple Subgraphs, deployed on multiple indexers.

πŸ’‘ Tip: You can compose any GraphQL sources, and not only Subgraphs!

Trivial composition can be done by adding more than one GraphQL source to your .graphclientrc.yml file, here's an example:

sources:
  - name: uniswapv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2
  - name: compoundv2
    handler:
      graphql:
        endpoint: https://api.thegraph.com/subgraphs/name/graphprotocol/compound-v2

As long as there a no conflicts across the composed schemas, you can compose it, and then run a single query to both Subgraphs:

query myQuery {
  # this one is coming from compound-v2
  markets(first: 7) {
    borrowRate
  }
  # this one is coming from uniswap-v2
  pair(id: "0x00004ee988665cdda9a1080d5792cecd16dc1220") {
    id
    token0 {
      id
    }
    token1 {
      id
    }
  }
}

You can also resolve conflicts, rename parts of the schema, add custom GraphQL fields, and modify the entire execution phase.

For advanced use-cases with composition, please refer to the following resources:

TypeScript Support

If your project is written in TypeScript, you can leverage the power of TypedDocumentNode and have a fully-typed GraphQL client experience.

The standalone mode of The GraphQL, and popular GraphQL client libraries like Apollo-Client and urql has built-in support for TypedDocumentNode!

The Graph Client CLI comes with a ready-to-use configuration for GraphQL Code Generator, and it can generate TypedDocumentNode based on your GraphQL operations.

To get started, define your GraphQL operations in your application code, and point to those files using the documents section of .graphclientrc.yml:

sources:
  -  # ... your Subgraphs/GQL sources here

documents:
  - ./src/example-query.graphql

You can also use Glob expressions, or even point to code files, and the CLI will find your GraphQL queries automatically:

documents:
  - './src/**/*.graphql'
  - './src/**/*.{ts,tsx,js,jsx}'

Now, run the GraphQL CLI build command again, the CLI will generate a TypedDocumentNode object under .graphclient for every operation found.

Make sure to name your GraphQL operations, otherwise it will be ignored!

For example, a query called query ExampleQuery will have the corresponding ExampleQueryDocument generated in .graphclient. You can now import it and use that for your GraphQL calls, and you'll have a fully typed experience without writing or specifying any TypeScript manually:

import { ExampleQueryDocument, execute } from '../.graphclient'

async function main() {
  // "result" variable is fully typed, and represents the exact structure of the fields you selected in your query.
  const result = await execute(ExampleQueryDocument, {})
  console.log(result)
}

You can find a TypeScript project example here.

Client-Side Mutations

Due to the nature of Graph-Client setup, it is possible to add client-side schema, that you can later bridge to run any arbitrary code.

This is helpful since you can implement custom code as part of your GraphQL schema, and have it as unified application schema that is easier to track and develop.

This document explains how to add custom mutations, but in fact you can add any GraphQL operation (query/mutation/subscriptions). See Extending the unified schema article for more information about this feature.

To get started, define a additionalTypeDefs section in your config file:

additionalTypeDefs: |
  # We should define the missing `Mutation` type
  extend schema {
    mutation: Mutation
  }

  type Mutation {
    doSomething(input: SomeCustomInput!): Boolean!
  }

  input SomeCustomInput {
    field: String!
  }

Then, add a pointer to a custom GraphQL resolvers file:

additionalResolvers:
  - './resolvers'

Now, create resolver.js (or, resolvers.ts) in your project, and implement your custom mutation:

module.exports = {
  Mutation: {
    async doSomething(root, args, context, info) {
      // Here, you can run anything you wish.
      // For example, use `web3` lib, connect a wallet and so on.

      return true
    }
  }
}

If you are using TypeScript, you can also get fully type-safe signature by doing:

import { Resolvers } from './.graphclient'

// Now it's fully typed!
const resolvers: Resolvers = {
  Mutation: {
    async doSomething(root, args, context, info) {
      // Here, you can run anything you wish.
      // For example, use `web3` lib, connect a wallet and so on.

      return true
    }
  }
}

export default resolvers

If you need to inject runtime variables into your GraphQL execution context, you can use the following snippet:

execute(
  MY_QUERY,
  {},
  {
    myHelper: {} // this will be available in your Mutation resolver as `context.myHelper`
  }
)

You can read more about client-side schema extensions here

You can also delegate and call Query fields as part of your mutation

License

Released under the MIT license.

More Repositories

1

graph-node

Graph Node indexes data from blockchains such as Ethereum and serves it over GraphQL
Rust
2,882
star
2

graph-tooling

Monorepo for various tools used by subgraph developers.
TypeScript
387
star
3

contracts

Contracts repository for The Graph protocol
TypeScript
316
star
4

indexer

Graph Protocol indexer components and infrastructure
TypeScript
236
star
5

graph-ts

TypeScript/AssemblyScript library for writing mappings for The Graph
214
star
6

example-subgraph

An example to help you get started with The Graph
Solidity
179
star
7

uniswap-subgraph

This is for uniswap-v1. If you are looking for the uniswap v2 subgraph, please go to https://github.com/uniswap/uniswap-v2-subgraph
TypeScript
105
star
8

everest

Registry of crypto projects
JavaScript
98
star
9

docs

Documentation for The Graph
MDX
78
star
10

graph-network-subgraph

The subgraph, the smart contracts, the tests, and documents for the Graph Explorer Decentralized Application
TypeScript
69
star
11

research

Research, proposals, papers, and specs
TeX
48
star
12

ipfs-sync

Script to sync files from one IPFS node to another
JavaScript
48
star
13

ethdenver-dapp

ETHDenver example dApp built on The Graph
JavaScript
43
star
14

erc20-subgraph

TypeScript
33
star
15

hardhat-graph

TypeScript
33
star
16

decentraland-subgraph

Decentraland data source for The Graph
TypeScript
29
star
17

common-ts

Common TypeScript library for use in Graph Protocol components
TypeScript
24
star
18

ens-rainbow

Rust
22
star
19

ens-subgraph

Official repo: https://github.com/ensdomains/ens-subgraph
TypeScript
22
star
20

aragon-subgraph

DAO - subgraph
TypeScript
21
star
21

mission-control-indexer

Technical indexer documentation and infrastructure templates for the Mission Control testnet
TypeScript
21
star
22

indexer-rs

Rewrite of indexer-service in Rust with TAP payments implementation
Rust
21
star
23

token-distribution

Token distribution contracts
TypeScript
21
star
24

agora

Cost model
Rust
20
star
25

rfcs

Graph Protocol RFCs and Engineering Plans
CSS
16
star
26

mission-control-curator

15
star
27

0x-subgraph

Subgraph that tracks events of the 0x protocol
TypeScript
15
star
28

example-subgraphs

TypeScript
14
star
29

allocation-optimizer

Agents and algorithms for optimizing Indexer decision problems.
Julia
14
star
30

firehose-cosmos

Cosmos Firehose integration
Go
11
star
31

support

Community support for the Hosted Service of The Graph
10
star
32

full-stack-graph-app

Deploy a full stack app leveraging The Graph with one click.
TypeScript
10
star
33

compound-v1-subgraph

Compound is an open-source protocol for algorithmic, efficient Money Markets on the Ethereum blockchain.
TypeScript
9
star
34

crypto-buddies

Gatsby React app interview challenge
JavaScript
8
star
35

augur-subgraph

Subgraph for the Augur protocol
TypeScript
8
star
36

qlog

A tool to summarize and analyze graph-node query logs
Rust
8
star
37

bitsausage

Bitsausage auction project
JavaScript
7
star
38

registry-starter

Starting point for apps that curate data
JavaScript
7
star
39

graph-network-analytics-subgraph

Analytics subgraph for Graph Network
TypeScript
6
star
40

stable-hash

A structured hash that is platform independent and has backward compatibility
Rust
6
star
41

common-subgraphs

Subgraphs that are generally useful and/or good examples
Shell
6
star
42

sportx-subgraph

SportX provides a sports betting platform for Ethereum.
TypeScript
6
star
43

block-oracle

Rust
6
star
44

graph-improvement-proposals

The canonical repository for The Graph's protocol governance. Read more about it: https://thegraph.com/ecosystem/governance/
5
star
45

FOAM-subgraph

The FOAM Proof of Location protocol empowers a permissionless and autonomous network of radio beacons that can offer secure location services.
TypeScript
5
star
46

hackathon-react-apollo-app

React, Apollo & Material UI example app
JavaScript
5
star
47

solidity-bindgen

Rust
5
star
48

hardhat-graph-demo

Demo repository for the hardhat-graph plugin
TypeScript
4
star
49

dharma-subgraph

Dharma protocol subgraph
TypeScript
4
star
50

hackathon-scaffold

A starting point for building dApps on The Graph
JavaScript
4
star
51

mutations

TypeScript
4
star
52

compound-subgraph-wrapper

A wrapper service that extends the Compound subgraph with custom resolvers
TypeScript
4
star
53

cosmoshub-ts

TypeScript
4
star
54

memefactory-subgraph

Memefactory subgraph for The Graph
TypeScript
3
star
55

eip-712-derive

Easy EIP-712
Rust
3
star
56

ipfs-mgm

IPFS cli for content syncing
Go
3
star
57

graph-node-docker

Preconfigured Docker image for running a Graph Node
Dockerfile
3
star
58

adchain-subgraph

Example subgraph project for querying the AdChain TCR
TypeScript
3
star
59

graph-local-state

Shell
2
star
60

thegarii

The Graph Arweave Integration Implementation
Rust
2
star
61

codex

2
star
62

maker-subgraph

Subgraph for maker
TypeScript
2
star
63

governance-subgraph

Subgraph for graph governance
TypeScript
2
star
64

firehose-arweave

The firehose integration with the garii
Go
2
star
65

autotasks

A collection of Defender Autotasks to interact with The Graph protocol contracts
TypeScript
2
star
66

query-examples

Provides examples of how to query Subgraphs published to The Graph Network in a variety of frameworks/languages
2
star
67

DEPRECATED-compound-v2-subgraph

Compound is an open-source protocol for algorithmic, efficient Money Markets on the Ethereum blockchain. DEPRECATED - see the repository under compound-finances github for the current repository https://github.com/graphprotocol/compound-v2-subgraph-1
TypeScript
2
star
68

everest-subgraph

Subgraph for the MetaCartel Everest project registry
1
star
69

hack-for-freedom

1
star
70

ethcc-hackathon

1
star
71

gitcoin-beyond-blockchain-hackathon

A three week virtual hackathon running from June 24th to July 10th powered by Gitcoin
1
star
72

network-support

Graph Network issues and feature requests
1
star
73

subgraph-oracle

The Subgraph Oracle
Rust
1
star
74

graph-pino

Pino log message formatter for The Graph
JavaScript
1
star