Just like how fries can be eaten alone 🍟 or eaten together with either ketchup or mayonnaise, this tutorial is both 1) a standalone tutorial for deploying and querying a subgraph. 2) a continuation from my previous tutorial on building a savings dapp.
Table of contents
- Prerequisites
- Introduction
- Tips to know about subgraphs
- Initialize Subgraph
- Tweak Code and Deploy
- Querying Subgraph
- Conclusion
Prerequisites
To build, deploy, and query from a subgraph you will need the following essentials:
- A deployed smart contract (whether verified or not),
- a crypto wallet,
- a subgraph studio account,
- and a cup of coffee ☕.
Before we start building the subgraph project, ensure you have the following above, done? now take a sip 🍵 and let's start building.
Introduction
A subgraph according to The Graph (which is a decentralized protocol for indexing and querying blockchain data) is a custom API built on blockchain data. They are queried using the GraphQL query language and are deployed to a Graph Node using the Graph CLI.
In simpler terms, a subgraph is like a customizable API for easily querying and getting specific data from a blockchain using GraphQL.
To get started, we need to have a subgraph studio account, which is very easy to do in 2 simple steps;
- Connect your wallet and
- Sign a message, then you should have a page like this;
Click on the Create a Subgraph button and enter a subgraph name. You have successfully created a subgraph. well-done!!
Tips to know about subgraphs
- Deleting a subgraph is not possible once it's created.
- Once a subgraph is created, the name cannot be changed.
- To enable faster and more easy querying of data from a subgraph, the contract associated with it should emit events.
- Subgraph mappings are written in AssemblyScript therefore using libraries like ether.js in the subgraph is not possible.
You can check out more of the tips or FAQs of a subgraph here
Initialize Subgraph
On the lower right side (you will have to scroll down) of your dashboard page of the subgraph you just created, steps on how to Initialize, Deploy and Build the subgraph are shown.
Following the steps;
- We need to install the graph cli before a subgraph can be initialized.
npm install -g @graphprotocol/graph-cli
or
yarn global add @graphprotocol/graph-cli
- After the installation, run the below
graph init --studio <SUBGRAPH_NAME>
Here you will be asked for the protocol, chain and other information of the contract you want to subgraph, following the last tutorial of writing, deploying and verifying our crypto-saves smart contract, these are the answers to the questions from the terminal:
- Protocol:
ethereum
- Subgraph Slug:
Crypto-Saves
(or any name you want) - Directory to create the subgraph in:
Crypto-Saves
(or anywhere you want) - Ethereum Network:
mumbai
(the chain where the contract was deployed) - Contract Address:
0x4b1d98af1af713456c1fc86afed67b94c4648f6d
(Contract Address)
- After you receive the Subgraph created message then run the next command;
graph auth --studio <DEPLOY_KEY>
cd <Name_of_subgraph>
- Using the events of the contract, the graph auto-generates the subgraph's code which needs little to no tweaking before deployment and publishing. The command below auto-generates the code and then builds it.
graph codegen && graph build
Now take another sip of that coffee ☕
Tweak Code and Deploy
Navigate to the src
folder in the subgraph root directory, then check the auto-generated file, the graph does splendid work and most times the file doesn't need tweaking and you can just deploy directly, but following my tutorial (Personal Crypto Savings Dapp) series, a little tweaking is needed.
Change the code in the schema.graphql
file in the root directory to the following updated code:
type EtherLocked @entity(immutable: true) {
id: ID!
name: String! # string
owner: Bytes! # address
amount: BigInt! # uint256
releaseTime: BigInt! # uint256
lockType: String!
locked: Boolean
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}
type EtherUnlocked @entity(immutable: true) {
id: ID!
name: String! # string
owner: Bytes! # address
amount: BigInt! # uint256
locked: Boolean
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}
type LockupTimeExtended @entity(immutable: true) {
id: ID!
name: String! # string
owner: Bytes! # address
releaseTime: BigInt! # uint256
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}
type OwnershipTransferred @entity(immutable: true) {
id: ID!
previousOwner: Bytes! # address
newOwner: Bytes! # address
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}
Here I updated the schema and changed some types for better querying results. After this run:
graph codegen
This would update the generated
folder with the new types and fields obtained from the update of the schema.graphql
, to complete this we will need to change some things in a file under the src
folder to:
import {
EtherLocked as EtherLockedEvent,
EtherUnlocked as EtherUnlockedEvent,
LockupTimeExtended as LockupTimeExtendedEvent,
OwnershipTransferred as OwnershipTransferredEvent,
} from "../generated/CryptoSaves/CryptoSaves";
import {
EtherLocked,
EtherUnlocked,
LockupTimeExtended,
OwnershipTransferred,
} from "../generated/schema";
export function handleEtherLocked(event: EtherLockedEvent): void {
let entity = EtherLocked.load(event.params.id.toString());
if (!entity) {
entity = new EtherLocked(event.params.id.toString());
}
entity.name = event.params.name;
entity.owner = event.params.owner;
entity.amount = event.params.amount;
entity.releaseTime = event.params.releaseTime;
entity.lockType = event.params.lockType;
entity.locked = true;
entity.blockNumber = event.block.number;
entity.blockTimestamp = event.block.timestamp;
entity.transactionHash = event.transaction.hash;
entity.save();
}
export function handleEtherUnlocked(event: EtherUnlockedEvent): void {
let entity = EtherLocked.load(event.params.id.toString());
if (!entity) {
return;
}
entity.locked = false;
entity.blockNumber = event.block.number;
entity.blockTimestamp = event.block.timestamp;
entity.transactionHash = event.transaction.hash;
entity.save();
}
export function handleLockupTimeExtended(event: LockupTimeExtendedEvent): void {
let entity = EtherLocked.load(event.params.id.toString());
if (!entity) {
return;
}
entity.releaseTime = event.params.releaseTime;
entity.blockNumber = event.block.number;
entity.blockTimestamp = event.block.timestamp;
entity.transactionHash = event.transaction.hash;
entity.save();
}
export function handleOwnershipTransferred(
event: OwnershipTransferredEvent
): void {
let entity = new OwnershipTransferred(event.transaction.hash.toString());
entity.previousOwner = event.params.previousOwner;
entity.newOwner = event.params.newOwner;
entity.blockNumber = event.block.number;
entity.blockTimestamp = event.block.timestamp;
entity.transactionHash = event.transaction.hash;
entity.save();
}
I am essentially doing another layer of checks before updating the fields after each event has been emitted.
Deploy
graph deploy --studio <SUBGRAPH_NAME>
This deploys your subgraph to production, enabling querying from either the playground or in your front-end application.
Querying Subgraph
A subgraph can be queried from the frontend application or from the playground that the subgraph studio offers, which I find very helpful.
Just like in the image you can write queries on the left side of the playground and get results on the right part. An explorer tab is also on the right part; which auto-generates queries by just clicking on the desired parameters.
Frontend Application querying can be done in 3 different ways:
I prefer to use the Apollo client, and I will use this in the frontend aspect of the crypto savings dapp in my next tutorial.
Conclusion
Congratulations on making it to the end of this tutorial!🎉 You should now have a good understanding of what subgraphs are, why they are useful, and how to initialize, deploy, and query them. With this knowledge, you can begin creating your subgraphs to interact with various contracts and gain insights into their behavior. Run into a wall? ask away in the comments.
Top comments (0)