Create a Subgraph
Before being able to use the Graph CLI, you need to create your subgraph in Subgraph Studio. You will then be able to setup your subgraph project and deploy it to the platform of your choice. Note that subgraphs that do not index Ethereum mainnet will not be published to The Graph Network.
The graph init command can be used to set up a new subgraph project, either from an existing contract on any of the public Ethereum networks or from an example subgraph. This command can be used to create a subgraph on the Subgraph Studio by passing in graph init –product subgraph-studio. If you already have a smart contract deployed to Ethereum mainnet or one of the testnets, bootstrapping a new subgraph from that contract can be a good way to get started. But first, a little about the networks The Graph supports.
The Graph Network supports subgraphs indexing mainnet Ethereum:
Additional Networks are supported in beta on the Hosted Service:
- xdai (now known as Gnosis Chain)
- matic (now known as Polygon)
- bsc (now known as BNB Chain)
The Graph’s Hosted Service relies on the stability and reliability of the underlying technologies, namely the provided JSON RPC endpoints. Newer networks will be marked as being in beta until the network has proven itself in terms of stability, reliability, and scalability. During this beta period, there is a risk of downtime and unexpected behaviour.
Remember that you will not be able to publish a subgraph that indexes a non-mainnet network to the decentralized Graph Network in Subgraph Studio.
From An Existing Contract
The following command creates a subgraph that indexes all events of an existing contract. It attempts to fetch the contract ABI from Etherscan and falls back to requesting a local file path. If any of the optional arguments are missing, it takes you through an interactive form.
SUBGRAPH_SLUG is the ID of your subgraph in Subgraph Studio, it can be found on your subgraph details page.
From An Example Subgraph
The second mode graph init supports is creating a new project from an example subgraph. The following command does this:
The example subgraph is based on the Gravity contract by Dani Grant that manages user avatars and emits NewGravatar or UpdateGravatar events whenever avatars are created or updated. The subgraph handles these events by writing Gravatar entities to the Graph Node store and ensuring these are updated according to the events. The following sections will go over the files that make up the subgraph manifest for this example.
The Subgraph Manifest
The subgraph manifest
subgraph.yaml defines the smart contracts your subgraph indexes, which events from these contracts to pay attention to, and how to map event data to entities that Graph Node stores and allows to query. The full specification for subgraph manifests can be found here.
For the example subgraph,
The important entries to update for the manifest are:
description: a human-readable description of what the subgraph is. This description is displayed by the Graph Explorer when the subgraph is deployed to the Hosted Service.
repository: the URL of the repository where the subgraph manifest can be found. This is also displayed by The Graph Explorer.
features: a list of all used feature names.
dataSources.source: the address of the smart contract the subgraph sources, and the ABI of the smart contract to use. The address is optional; omitting it allows to index matching events from all contracts.
dataSources.source.startBlock: the optional number of the block that the data source starts indexing from. In most cases, we suggest using the block in which the contract was created.
dataSources.mapping.entities: the entities that the data source writes to the store. The schema for each entity is defined in the schema.graphql file.
dataSources.mapping.abis: one or more named ABI files for the source contract as well as any other smart contracts that you interact with from within the mappings.
dataSources.mapping.eventHandlers: lists the smart contract events this subgraph reacts to and the handlers in the mapping—./src/mapping.ts in the example—that transform these events into entities in the store.
dataSources.mapping.callHandlers: lists the smart contract functions this subgraph reacts to and handlers in the mapping that transform the inputs and outputs to function calls into entities in the store.
dataSources.mapping.blockHandlers: lists the blocks this subgraph reacts to and handlers in the mapping to run when a block is appended to the chain. Without a filter, the block handler will be run every block. An optional call-filter can be provided by adding a
filter field with
kind: call to the handler. This will only run the handler if the block contains at least one call to the data source contract.
A single subgraph can index data from multiple smart contracts. Add an entry for each contract from which data needs to be indexed to the
The triggers for a data source within a block are ordered using the following process:
- Event and call triggers are first ordered by transaction index within the block.
- Event and call triggers within the same transaction are ordered using a convention: event triggers first then call triggers, each type respecting the order they are defined in the manifest.
- Block triggers are run after event and call triggers, in the order they are defined in the manifest.
These ordering rules are subject to change.
Getting The ABIs
The ABI file(s) must match your contract(s). There are a few ways to obtain ABI files:
- If you are building your own project, you will likely have access to your most current ABIs.
- If you are building a subgraph for a public project, you can download that project to your computer and get the ABI by using
truffle compile or using solc to compile.
- You can also find the ABI on Etherscan, but this isn’t always reliable, as the ABI that is uploaded there may be out of date. Make sure you have the right ABI, otherwise running your subgraph will fail.
The GraphQL Schema
The schema for your subgraph is in the file schema.graphql. GraphQL schemas are defined using the GraphQL interface definition language. If you’ve never written a GraphQL schema, it is recommended that you check out this primer on the GraphQL type system. Reference documentation for GraphQL schemas can be found in the GraphQL API section.
Before defining entities, it is important to take a step back and think about how your data is structured and linked. All queries will be made against the data model defined in the subgraph schema and the entities indexed by the subgraph. Because of this, it is good to define the subgraph schema in a way that matches the needs of your dapp. It may be useful to imagine entities as “objects containing data”, rather than as events or functions.
With The Graph, you simply define entity types in schema.graphql, and Graph Node will generate top level fields for querying single instances and collections of that entity type. Each type that should be an entity is required to be annotated with an @entity directive. By default, entities are mutable, meaning that mappings can load existing entities, modify them and store a new version of that entity. Mutability comes at a price, and for entity types for which it is known that they will never be modified, for example, because they simply contain data extracted verbatim from the chain, it is recommended to mark them as immutable with @entity(immutable: true). Mappings can make changes to immutable entities as long as those changes happen in the same block in which the entity was created. Immutable entities are much faster to write and to query, and should therefore be used whenever possible.
Optional and Required Fields
Entity fields can be defined as required or optional. Required fields are indicated by the
! in the schema. If a required field is not set in the mapping, you will receive this error when querying the field:
Each entity must have an
id field, which must be of type
String!. It is generally recommended to use Bytes!, unless the id contains human-readable text, since entities with Bytes! id’s will be faster to write and query as those with a
id field serves as the primary key, and needs to be unique among all entities of the same type. For historical reasons, the type
ID! is also accepted and is a synonym for
For some entity types the id is constructed from the id’s of two other entities; that is possible using concat, e.g., let id = left.id.concat(right.id) to form the id from the id’s of left and right. Similarly, to construct an id from the id of an existing entity and a counter count, let id = left.id.concatI32(count) can be used. The concatenation is guaranteed to produce unique id’s as long as the length of left is the same for all such entities, for example, because left.id is an Address.