The Graph Tutorial: Creating a Subgraph
Tags: thegraph, smartcontract, ethereum, web3
Published: 2022-02-15 19:13:47

Before you continue, I'm assuming you know what [The Graph](https://thegraph.com/) is and what problems it tackles. If you haven't, I highly recommend checking out - [Why The Graph](https://dev.to/nvn/the-graph-tutorial-why-the-graph-ka3).

## Installation
First of all, you'll need **Graph CLI** tool. Install it via:

**yarn**
```
yarn global add @graphprotocol/graph-cli
```

or, **npm**
```
npm install -g @graphprotocol/graph-cli
```

In this tutorial I'll be using a simple [Gravity](https://github.com/graphprotocol/example-subgraph/blob/master/contracts/Gravity.sol) contract as an example.

Before you can use the already installed Graph CLI, head over to the [Subgraph Studio](https://thegraph.com/studio/) and connect your account. You'll need a wallet like [MetaMask](https://metamask.io/) already installed on your browser. So make sure it is there. 

Time to create a new subgraph in the studio now. Note that the Gravity contract is already deployed on Ethereum Mainnet. You can inspect it on [Etherscan](https://etherscan.io/address/0x2e645469f354bb4f5c8a05b3b30a929361cf77ec#code). It is deployed at the following address:
```
0x2E645469f354BB4F5c8a05B3b30A929361cf77eC
```

With this information known, go ahead and create a new subgraph in the studio. Select the Ethereum Mainnet as the network to index the contract data from. And fill-in a sensible name of subgraph like - `Gravity`. Hit continue. This will take you to dashboard where you may fill optional fields like description, website, etc. I'm gonna skip it for sake of brevity.

## Initializing Subgraph

The Graph CLI provides `graph init` command to initialize the subgraph:
```
graph init \
  --product subgraph-studio
  --from-contract <CONTRACT_ADDRESS> \
  [--network <ETHEREUM_NETWORK>] \
  [--abi <FILE>] \
  <SUBGRAPH_SLUG> [<DIRECTORY>]
```
I recommend checking what each option does by running `graph init --help` in console.

Now copy the generated subgraph slug from the dashboard. It is `gravity` for me. We need it for initializing this subgraph:
```
graph init --studio gravity
```
_(Note that `--studio` option above is nothing but short-hand for `--product subgraph-studio`)_

This will prompt for multiple inputs like network name, contract address, ABI etc. In this case, we're using Ethereum Mainnet network, with an already deployed Gravity contract. Make sure your input match following:
```
✔ Protocol · ethereum
✔ Subgraph slug · gravity
✔ Directory to create the subgraph in · gravity
✔ Ethereum network · mainnet
✔ Contract address · 0x2E645469f354BB4F5c8a05B3b30A929361cf77eC
✔ Fetching ABI from Etherscan
✔ Contract Name · Gravity
```
If not provided inline to `graph init`, the cli will try to fetch the ABI of the contract from Etherscan. If not found, you'll need to provide path to the ABI file of the contract. ABI is easy to generate for your contract through dev tools like [Hardhat](https://hardhat.org/) or [Truffle](https://trufflesuite.com/), one of which you might already be using. Or, you can simply copy it from IDE, if you're using [Remix](https://remix.ethereum.org/). In our case, ABI was already available on Etherscan.

Initialization creates a new directory containing multiple auto-generated files for you as a starting point. These include some configuration files specifically tailored for the provided contract through its ABI. Open this project in your favorite editor and let's start with next steps.

## Configuring Subgraph Manifest
The subgraph manifest file - `subgraph.yaml` is a [YAML](https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/) file located at the root path. This describes the data-sources your subgraph will index. In this case, data-source is Gravity contract on Ethereum Mainnet. Replace the contents of file with:

```yaml
specVersion: 0.0.4
description: Gravatar for Ethereum
repository: https://github.com/graphprotocol/example-subgraph
schema:
  file: ./schema.graphql
dataSources:
  - kind: ethereum/contract
    name: Gravity
    network: mainnet
    source:
      address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC'
      abi: Gravity
      startBlock: 6175244
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.6
      language: wasm/assemblyscript
      entities:
        - Gravatar
      abis:
        - name: Gravity
          file: ./abis/Gravity.json
      eventHandlers:
        - event: NewGravatar(uint256,address,string,string)
          handler: handleNewGravatar
        - event: UpdatedGravatar(uint256,address,string,string)
          handler: handleUpdatedGravatar
      file: ./src/mapping.ts
```
We're gonna keep this simple. You can find out about what each of the available fields mean at full-specification [here](https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md). Though some notable ones are:

- **`schema.file`**: Path to a GraphQL schema file defining what data is stored for your subgraph. This also generated with initialization.
- **`dataSources`**: List of sources from which to index data. You can use a single subgraph to index the data from multiple smart-contracts.
- **`dataSources.source`**: Address and ABI of the source smart-contract. `startBlock` indicates from which block of the blockchain to start indexing data from. Its value is usually the block at which the contract was deployed.
- **`dataSources.mapping.entities`**: These are the entities that are written to The Graph network's storage. These entities are defined in a GraphQL schema file - `schema.graphql`.
- **`dataSources.mapping.abis`**: ABI files for the source contract as well as  any other smart contracts that you interact with from within the mappings (`mapping.ts` file that you will write).
- **`dataSources.mapping.eventHandlers`**: List of the smart-contract events that this subgraph will react to with corresponding handlers that are defined in mappings file (`./src/mapping.ts`). It is these handlers that will transform the events to proper entities to be saved.
- **`dataSources.mapping.callHandlers`** (not used here): Lists of the smart-contract functions the subgraph reacts to with corresponding handlers. These handlers are defined in the mapping too. They transform the inputs and outputs to function calls into entities in the store.
- **`dataSources.mapping.blockHandlers`** (not used here): Blocks with handlers the subgraph will react to. With no `filter` it will react every time new block is appended to chain. However, a `call` filter will make the handler run only  if the block contains at least one call to the data source contract.


## Defining Entities
The entities are defined in a GraphQL schema file - `schema.graphql`, also located the root path. These entities indicate what data is stored for this subgraph and how to query it. If you're new to GraphQL, check out at least a primer on it on [official website](https://graphql.org/learn/) or if you're feeling adventurous like me try this [awesome course](https://odyssey.apollographql.com/) by Apollo team.

Alright, I assume you have at least basic knowledge of GraphQL by now. But before defining entities think about the structure of your data, like how you would've done while designing schemas for a DB. All the queries made from user-facing app will me made against this data model. **Rather than imagining these entities in smart-contract lingo i.e. imagining entities as "events" or contract "functions", imagine entities as data "objects".** Your app will query on these objects.

In our case, for example, it can be seen in `subgraph.yaml` above that, the network is instructed to react to `NewGravatar`/`UpdateGravatar` through handlers - `handleNewGravatar`/`handleUpdateGravatar` (defined in `mapping.ts`). These handlers eventually convert event data to a related entity (`Gravatar`) defined in `schema.graphql`, which will then be saved/updated in storage.
Here the entity is the `Gravatar` object, not the individual events. A good schema would be:
```graphql
type Gravatar @entity {
  id: ID!
  owner: Bytes
  displayName: String
  imageUrl: String
}
```
Open the `schema.graphql` and paste above to finish defining the entity.

## Writing Mappings

The job of mappings file (in `./src/mapping.ts`) is to convert data coming from blockchain (e.g. events) to an entity defined in the `schema.graphql` and write it to store. The mappings are written in [AssemblyScript](https://www.assemblyscript.org/) which can be compiled to [WASM](https://webassembly.org/). It is a stricter subset of [TypeScript](https://www.typescriptlang.org/). If you've never written any TypeScript ever, I recommend getting familiar with the syntax [here](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html).

All the APIs available to write these mappings is described in [AssemblyScript API](https://thegraph.com/docs/en/developer/assemblyscript-api) docs. You might want to check out at least [Built-in Types](https://thegraph.com/docs/en/developer/assemblyscript-api#built-in-types) and [Store API](https://thegraph.com/docs/en/developer/assemblyscript-api#store-api) specifically to start.

If you inspect `./src/mapping.ts`, you can see already pre-filled code that was generated by the `graph init` command ran earlier. But this is based off the `subgraph.yaml` and `schema.graphql` before editing. Since we changed contents of these two we need to generate types corresponding to updated entities. This is so we can use these generated types/classes to write our `mapping.ts`. The Graph CLI provides the `codegen` command for this purpose. Run:
```
graph codegen
```
_(This is also pre-configured in `package.json` so that you can run `yarn codegen` or `npm codegen` which does the same)_

You can see the generated types/classes corresponding to schema as well as source contract(s) in the `./generated` directory.

Alright, now is the time to write the handlers - `handleNewGravatar` and `handleUpdateGravatar` that were specified at `dataSources.mapping.eventHandlers` in the `subgraph.manifest`. Open the `mapping.ts` and replace with following:
```ts
// Import event classes
import {
  NewGravatar,
  UpdatedGravatar
} from "../generated/Gravity/Gravity";

// Import entity class
import { Gravatar } from "../generated/schema";

export function handleNewGravatar(event: NewGravatar): void {
  // Use id field from emitted event as unique id for the entity
  const id = event.params.id.toHex();

  // Create a new Gravatar Entity with unique id
  const gravatar = new Gravatar(id);

  // Set Gravatar Entity fields
  gravatar.owner = event.params.owner;
  gravatar.displayName = event.params.displayName;
  gravatar.imageUrl = event.params.imageUrl;

  // Save entity to store
  gravatar.save();
}

export function handleUpdatedGravatar(event: UpdatedGravatar): void {
  // Use proper id to load an entity from store
  const id = event.params.id.toHex();

  // Load the entity to be updated
  let gravatar = Gravatar.load(id);

  // Create the entity if it doesn't already exist
  if (!gravatar) {
    gravatar = new Gravatar(id);
  }

  // Set updated fields to entity
  gravatar.owner = event.params.owner;
  gravatar.displayName = event.params.displayName;
  gravatar.imageUrl = event.params.imageUrl;

  // Save updated entity to store
  gravatar.save();
}
```
Note the imports above are the same types/classes generated earlier by `graph codegen`. The handlers receive `event` of type `NewGravatar`/`UpdateGravatar`, which are subclass of a parent `ethereum.Event` class. The event data fields is available in `event.params` object.

Each entity requires a unique id assigned to it. Here, hex of `event.params.id` (type [`BigInt`](https://thegraph.com/docs/en/developer/assemblyscript-api#big-int)) from contract event is used. However, uniqueness can also be guaranteed by choosing id from event metadata like:
- `event.transaction.from.toHex()`
- `event.transaction.hash.toHex() + "-" + event.logIndex.toString()`

After setting/updating fields to entity, [Store API](https://thegraph.com/docs/en/developer/assemblyscript-api#store-api) provides methods like `load` and `save` on `Entity` (inherited by `Gravatar`) types to retrieve and save the entity to store.

## Publishing the Subgraph

Now that all of the configuration is done, let's proceed to deploying this subgraph. But before doing that make sure that `mapping.ts` is checked without any errors or bugs. It is not checked by code generation step. As a necessary precaution run the build command:
```
graph build
```
_(Or `yarn build` / `npm build` as this command must also be configured in `package.json` too)_

This compiles the subgraph to WebAssembly. If there is a syntax error somewhere, it should fail. Otherwise, build succeeds, creating a `./build` directory with built files.

For deploying the subgraph to [Subgraph Studio](https://thegraph.com/studio/), you'll need the deploy key of the subgraph. Go to the already created Gravity subgraph's dashboard in the studio and copy its **deploy key**. Now authenticate from cli with this key:
```
graph auth --studio <DEPLOY KEY>
```
This stores the access token to system's keychain.

Finally, deploy to studio by providing subgraph slug:
```
graph deploy --studio gravity
```
This will ask for a version label for current version of your subgraph. You can enter whatever version semantic you prefer or go with `v0.0.1`.
```
✔ Version Label (e.g. v0.0.1) · v0.0.1
.
.
✔ Apply migrations
✔ Load subgraph from subgraph.yaml
.
✔ Compile subgraph
.
✔ Write compiled subgraph to build/
.
.
✔ Upload subgraph to IPFS
```

Note that the subgraph is not yet published! It is currently deployed to the studio. After deployment it will start syncing data from the chain and take some time, after which you can play around and test it. Then, if everything seems to be ok, hit **Publish**. This will publish your subgraph to the production on the Graph network and you'll get an API endpoint, like below, where you query the data:
```
https://api.studio.thegraph.com/query/<ID>/<SUBGRAPH_NAME>/<VERSION>
```
Note that every query will be **charged in GRT tokens** as fees.

However, for testing purposes the studio also provides a temporary, rate limited API endpoint. For example, for me it was - `https://api.studio.thegraph.com/query/21330/gravity/v0.0.1`. Let's see if it works. You can use this online GraphQL client - [GraphiQL](https://graphiql-online.com/) for that. Enter your testing API endpoint and make the following query to get a gravatar by id and hit run:
```graphql
query MyQuery {
  gravatar(id: "0xa") {
    displayName
    id
    imageUrl
    owner
  }
}
```

which outputs:
```json
{
  "data": {
    "gravatar": {
      "displayName": "duqd",
      "id": "0xa",
      "imageUrl": "https://ucarecdn.com/ddbebfc0-...",
      "owner": "0x48c89d77ae34ae475e4523b25ab01e363dce5a78"
    }
  }
}
```
It worked!

This was a fairly simple example to familiarize with the workings of The Graph. You might define a more complex GraphQL schema, mappings and the subgraph manifest for a production application. Feel free to dive into the [official docs](https://thegraph.com/docs/en/developer/quick-start/) for reference.

See full code at GitHub repo [here](https://github.com/theNvN/gravity-subgraph).

_Hope you learned some awesome stuff! 😎_

_Feel free to catch me [here](https://linktr.ee/thenvn)!_