
Thegraph
- 7 installs
- 4 repo stars
- Updated February 25, 2026
- hairyf/blockchain-master
Index blockchain data into queryable subgraphs with The Graph - manifest, GraphQL schema, AssemblyScript mappings, templates, and Substreams.
About
The Graph indexes on-chain data into subgraphs defined by a manifest, GraphQL schema, and AssemblyScript mappings, queried over GraphQL. A developer uses it to build and query blockchain data indexers.
- subgraph.yaml manifest, schema, and AssemblyScript mappings
- Data-source templates and Substreams for parallel multi-chain indexing
Thegraph by the numbers
- 7 all-time installs (skills.sh)
- Ranked #331 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Jul 13, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hairyf/blockchain-master --skill thegraphAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 7 |
|---|---|
| repo stars | ★ 4 |
| Last updated | February 25, 2026 |
| Repository | hairyf/blockchain-master ↗ |
What it does
Index blockchain data into queryable subgraphs with The Graph - manifest, GraphQL schema, AssemblyScript mappings, templates, and Substreams.
Files
Skill based on The Graph docs (graphprotocol/docs), generated 2026-02-09. Official docs: https://thegraph.com/docs
The Graph indexes blockchain data into queryable subgraphs. Subgraphs are defined by a manifest (subgraph.yaml), a GraphQL schema, and AssemblyScript mappings; they are queried via GraphQL. Substreams provide parallel, multi-chain indexing with multiple sinks.
Core References
| Topic | Description | Reference |
|---|---|---|
| Subgraph Manifest | Data sources, event/call/block handlers, indexer hints | core-subgraph-manifest |
| Schema | Entities, scalars, relationships, @derivedFrom, fulltext | core-schema |
| Mappings | AssemblyScript handlers, graph-ts, codegen, store API | core-mappings |
| GraphQL API | Queries, filtering, pagination, sorting, time-travel | core-graphql-api |
| Subgraph ID vs Deployment ID | When to use which for querying; version pinning | core-deployment-id-vs-subgraph-id |
Best Practices
| Topic | Description | Reference |
|---|---|---|
| Performance | Avoid eth_calls, immutable entities, Bytes ids, @derivedFrom, pruning | best-practices-performance |
| Grafting & Hotfix | Reuse indexed data for hotfix deployment; when to use and avoid | best-practices-grafting-hotfix |
| Timeseries & Aggregations | @entity(timeseries), @aggregation, intervals, dimensions | best-practices-timeseries |
| Querying | Static queries, variables, @include/@skip, batching, fragments | best-practices-querying |
Features
Subgraph Features
| Topic | Description | Reference |
|---|---|---|
| Data Source Templates | Dynamic contracts, factory pattern, create/createWithContext | features-data-source-templates |
| Deployment & Publishing | Deploy to Studio, publish to network, CLI, GRT signal | features-deployment-publishing |
| Querying from Application | Endpoints, graph-client, Apollo, URQL, API keys | features-querying-from-application |
| Unit Testing (Matchstick) | graph test, describe/test, assertions, mocking, coverage | features-unit-testing |
| Subgraph Composition | Combine up to 5 source subgraphs; immutable entities, same chain | features-subgraph-composition |
| Subgraph Linter | Static analysis: entity overwrite, null safety, undeclared eth_calls | features-subgraph-linter |
| Debug Forking | Fork remote store at block X for fast local debugging | features-debug-forking |
| Graph Node Dev (gnd) | Local node with optional IPFS, auto Postgres, --watch | features-graph-node-dev |
Indexing
| Topic | Description | Reference |
|---|---|---|
| Substreams | Parallel indexing, Rust/WASM, multi-chain, multi-sink | features-substreams |
| Substreams Sinks | SQL, PubSub, stream, KV; official vs community | features-substreams-sinks |
External Links
Generation Info
- Source:
sources/thegraph(https://github.com/graphprotocol/docs) - Git SHA:
f957884a16096b5e75a8024d29632c8a3c5fc7fd - Generated: 2026-02-09
- Docs used: website/src/pages/en/ (subgraphs, substreams, indexing)
- More workflow: 2026-02-25 (3 passes; source read from same SHA)
The Graph — Grafting for Hotfix Deployment
Grafting lets you deploy a new subgraph that reuses the indexed data of an existing (e.g. failed) deployment up to a given block, then continues indexing from there. Use it for quick hotfixes without re-indexing from genesis.
When to use grafting
- Critical indexing error: The current deployment stopped at a block; you fix the mapping and want to resume without losing history.
- Minimize downtime: Deploy the fixed subgraph immediately; it copies data up to the last good block and continues.
- Data preservation: Historical entities from the base deployment are copied; no need to re-index from block 0.
Manifest configuration
Declare grafting under features and add a graft block with the base deployment (Deployment ID, not Subgraph ID) and the block number (last successfully indexed block). Set the new data source's startBlock to one block after that.
specVersion: 1.3.0
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum/contract
name: NewSmartContract
network: sepolia
source:
address: '0xNewContractAddress'
abi: Lock
startBlock: 6000001 # one block after last indexed
mapping:
kind: ethereum/events
apiVersion: 0.0.9
language: wasm/assemblyscript
entities: [Withdrawal]
abis:
- name: Lock
file: ./abis/Lock.json
eventHandlers:
- event: Withdrawal(uint256,uint256)
handler: handleWithdrawal
file: ./src/lock.ts
features:
- grafting
graft:
base: QmBaseDeploymentID # Deployment ID of the base (failed) subgraph
block: 6000000 # last successfully indexed blockWorkflow
1. Identify the last good block (e.g. from Studio logs). 2. Implement the fix in mappings (and schema if compatible). 3. Update manifest: new/updated data sources, startBlock, features: [grafting], graft.base (Deployment ID), graft.block. 4. Deploy with graph deploy (or Studio). 5. Verify indexing from the graft block; then plan a non-grafted republish for long-term maintenance.
When to avoid grafting
- Schema incompatibility: Changing field types or removing fields; grafted data won't match the new schema.
- Major mapping changes: Very different event/handler logic can make grafted state inconsistent.
- Decentralized network (mainnet): Grafting is not recommended for subgraphs published to The Graph Network; prefer full re-index for reliability.
Key points
- Use Deployment ID for
graft.base, not Subgraph ID (find it in Studio or IPFS). - Choose the graft block carefully (last correctly processed block) to avoid data loss or duplication.
- After the hotfix is stable, deploy a new version without grafting for ongoing maintenance.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/best-practices/grafting-hotfix/
-->
The Graph — Performance Best Practices
Avoid eth_calls
Subgraphs are optimized for indexing events. eth_calls (contract read calls from mappings) slow indexing and depend on node responsiveness. Prefer contracts that emit all needed data in events. If you must call:
- Declare calls in the manifest (specVersion >= 1.2.0) so graph-node runs them in parallel before handlers and caches results:
eventHandlers:
- event: TransferWithPool(...)
handler: handleTransferWithPool
calls:
ERC20.poolInfo: ERC20[event.address].getPoolInfo(event.params.to)The handler still binds and calls; the result comes from cache.
Immutable entities and Bytes as IDs
- Use
@entity(immutable: true)for entities that are never updated; faster writes and queries. - Use
id: Bytes!instead ofString!when the id is not human-readable; faster and smaller.
@derivedFrom
Store one-to-many on the "many" side only; use @derivedFrom(field: "token") on the "one" side. Avoid storing arrays of entities on the many side—indexing and querying are much slower.
Pruning
indexerHints.prune: auto— minimal history; best query performance; no time-travel or grafting.- Use
prune: neveror a block count if you need time-travel queries or grafting.
Key points
- Emit data in events rather than reading via eth_call when you control or can change the contract.
- Call handlers and block handlers with
filter: kind: callrequire Parity tracing; not supported on BNB, Arbitrum, etc.—use event handlers there.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/best-practices/avoid-eth-calls/
- https://thegraph.com/docs/en/subgraphs/best-practices/immutable-entities-bytes-as-ids/
- https://thegraph.com/docs/en/subgraphs/best-practices/derivedfrom/
- https://thegraph.com/docs/en/subgraphs/best-practices/pruning/
-->
The Graph — Querying Best Practices
Write static, well-structured GraphQL queries and use variables, conditional fields, and batching so subgraph queries are efficient and cacheable.
Static queries and variables
- Avoid building query strings with string interpolation (e.g.
${id},${fields.join()}). It blocks server-side caching and static analysis. - Use static query strings and pass values via variables:
query GetToken($id: ID!) { token(id: $id) { id owner } }withvariables: { id }. - Variables are validated and sanitized by the API; tools can type-check and generate types from the static query.
Conditional fields
- @include(if: $bool): Include a field only when the variable is true.
- @skip(if: $bool): Omit a field when the variable is true.
Use these so one static query fetches only the fields needed for the current view, keeping payloads small.
Ask only for what you need
- List every field you need; there is no "fetch all fields."
- Limit collection sizes with first (and skip if needed). Defaults can return up to 100 entities per collection; nested collections multiply that (e.g. 100 tokens × 100 transactions each). Set
firston nested collections to match what the UI actually uses.
One query for multiple records
- For several entities by id: use the plural field with
where: { id_in: [id1, id2, id3] }instead of multiple single-entity queries. - For filtered lists: use
where(e.g.volume_gt: "...") on the plural field.
Combine operations in one request
- Put multiple root fields in a single query so one HTTP request returns all needed data (e.g.
tokens(first: 50) { ... }andcounters { ... }in the samequery). Reduces round trips and keeps the client simple.
Fragments
- Define fragments on entity types for repeated selections (e.g.
fragment DelegateItem on Transcoder { id active status }) and spread them:newDelegate { ...DelegateItem },oldDelegate { ...DelegateItem }. - Use one fragment per logical "shape" of data; improves readability and type generation. Fragments must be on the correct type and cannot be on scalars (e.g. not on
BigInt).
GraphQL rules (summary)
- Use each query name and each field once per operation as required by the API.
- Complex types need a selection set; variables must match argument types.
- Prefer graph-client (or a typed client) for cross-subgraph queries, block tracking, and pagination.
Key points
- Static queries + variables improve caching, security, and tooling.
- Use
first(and@include/@skip) to limit payload size; batch with one query and fragments for clarity and fewer requests.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/querying/best-practices/
-->
The Graph — Timeseries and Aggregations
Timeseries entities store immutable, time-ordered data points; aggregation entities compute sums, counts, min/max, etc. over intervals. This reduces mapping work and speeds up queries (spec 1.1.0+).
Timeseries entity
- Annotation:
@entity(timeseries: true). Timeseries entities are always immutable. - Required fields:
id: Int8!(auto-incremented),timestamp: Timestamp!(block timestamp). - Use: Raw data points (e.g. price, amount per block or event).
type Data @entity(timeseries: true) {
id: Int8!
timestamp: Timestamp!
amount: BigDecimal!
}Aggregation entity
- Annotation:
@aggregation(intervals: ["hour", "day"], source: "Data")— source is the timeseries entity name. - Fields:
id,timestamp, plus fields with@aggregate(fn: "sum"|"count"|"min"|"max"|"first"|"last", arg: "fieldOrExpression", cumulative: true?). - arg: Field name from the source entity or an expression (e.g.
priceUSD * amount). Supports SQL-like expressions.
type Stats @aggregation(intervals: ["hour", "day"], source: "Data") {
id: Int8!
timestamp: Timestamp!
sum: BigDecimal! @aggregate(fn: "sum", arg: "amount")
count: Int8! @aggregate(fn: "count", cumulative: true)
}Dimensions
Non-aggregated fields in the aggregation entity group data (e.g. per token). Include them in the source timeseries and in the aggregation entity; use them in query where to filter by dimension and time range.
Querying
Use the aggregation's query field with interval, where (dimensions + timestamp_gte / timestamp_lt in microseconds). Optional current can include the current partial interval.
Key points
- Use timeseries for high-volume, append-only data; aggregations for precomputed rollups.
- Prefer database-managed aggregations over doing the same in mapping code.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/best-practices/timeseries/
- https://github.com/graphprotocol/graph-node/blob/master/docs/aggregations.md
-->
The Graph — Subgraph ID vs Deployment ID
Queries can target a subgraph by Subgraph ID (all versions) or Deployment ID (one version). Both are visible in Subgraph Studio.
Deployment ID
- What it is: IPFS hash of the compiled subgraph manifest (that version's deployment).
- Querying: Use the deployments endpoint, e.g.
https://gateway-arbitrum.network.thegraph.com/api/<api-key>/deployments/id/<DEPLOYMENT_ID>. - Behavior: Pins a specific version. No automatic switch to a new deployment when you publish again.
- Use when: Production when you need stable, predictable results and are okay updating the endpoint when you intentionally move to a new version.
Subgraph ID
- What it is: Stable identifier for the subgraph; same across all versions.
- Querying: Use the subgraphs endpoint, e.g.
https://gateway-arbitrum.network.thegraph.com/api/<api-key>/subgraphs/id/<SUBGRAPH_ID>. - Behavior: Resolves to the latest published version. After a new publish, indexers need time to sync; queries may still hit the previous version temporarily. New versions can introduce breaking schema changes.
- Use when: Development or when you always want the latest version and can handle sync delay and possible breaking changes.
Comparison
| Aspect | Deployment ID | Subgraph ID |
|---|---|---|
| Version | Single deployment | Latest version |
| Maintenance | Update endpoint when you adopt a new version | No endpoint change |
| Best for | Production, stability | Development, "always latest" |
Grafting and other references
For grafting, the graft.base in the manifest must be the Deployment ID of the base subgraph, not the Subgraph ID. Find the Deployment ID in Studio or from the compiled manifest's IPFS hash.
Key points
- Prefer Deployment ID in production for version control and consistency.
- Use Subgraph ID when you want automatic latest and can tolerate sync lag and breaking changes.
- Never use Subgraph ID as
graft.base; use Deployment ID only.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/querying/subgraph-id-vs-deployment-id/
-->
The Graph — GraphQL API
Subgraphs expose a read-only GraphQL API. Each entity type gets root fields entity(id: ID) and entities(...).
Single and collection queries
{ token(id: "1") { id owner } }
{ tokens(first: 10) { id owner } }Pagination
first: n— limit; default sort is by id ascending.skip: n— offset (avoid large skip for performance).- Prefer cursor-style:
where: { id_gt: $lastID }withfirst: 1000and pass last id from previous page.
query manyTokens($lastID: String) {
tokens(first: 1000, where: { id_gt: $lastID }) { id owner }
}Sorting
{ tokens(orderBy: price, orderDirection: asc) { id price } }
{ tokens(orderBy: owner__name, orderDirection: asc) { id owner { name } } }Filtering (where)
- Equality:
where: { outcome: "failed" } - Numeric:
_gt,_gte,_lt,_lte(e.g.deposit_gt: "10000000000") - String:
_contains,_contains_nocase,_starts_with,_ends_with,_in,_not_in - Entity:
where: { application_: { id: "1" } } - Block:
_change_block: { number_gte: 100 } - Logic:
and: [...],or: [...](comma in same object = AND)
Time-travel
Query state at a block:
{ challenges(block: { number: 8000000 }) { challenger outcome } }
{ challenges(block: { hash: "0x5a0b54..." }) { challenger outcome } }Requires unpruned history (see manifest indexerHints.prune).
Fulltext search
If the schema defines a fulltext directive (e.g. bandSearch):
{ bandSearch(text: "breaks & electro") { id name description } }Operators: & (and), | (or), <-> (follow-by distance), :* (prefix, min 2 chars).
Metadata
{ _meta(block: { number: 123987 }) { block { number hash timestamp } deployment hasIndexingErrors } }Key points
- Filter operators:
_not,_in,_not_in,_contains, etc.; type support varies (e.g. Boolean only_not,_in,_not_in). - Prefer
andand cursor-based pagination over largeskipor heavyorfor performance.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/querying/graphql-api/
-->
The Graph — Mappings
Mappings are written in AssemblyScript (subset of TypeScript), compiled to WASM. They transform chain data into entities defined in the schema.
Handler signature
For each handler in subgraph.yaml, export a function with the same name. Event handlers receive the event type (from codegen); call handlers receive a *Call type; block handlers receive ethereum.Block.
import { NewGravatar, UpdatedGravatar } from '../generated/Gravity/Gravity'
import { Gravatar } from '../generated/schema'
export function handleNewGravatar(event: NewGravatar): void {
let gravatar = new Gravatar(event.params.id)
gravatar.owner = event.params.owner
gravatar.displayName = event.params.displayName
gravatar.imageUrl = event.params.imageUrl
gravatar.save()
}
export function handleUpdatedGravatar(event: UpdatedGravatar): void {
let gravatar = Gravatar.load(event.params.id)
if (gravatar == null) gravatar = new Gravatar(event.params.id)
gravatar.owner = event.params.owner
gravatar.displayName = event.params.displayName
gravatar.imageUrl = event.params.imageUrl
gravatar.save()
}Codegen
Run before build/deploy after schema or ABI changes:
graph codegen [--output-dir <OUTPUT_DIR>] [<MANIFEST>]This generates: contract/event/call types under generated/<DataSourceName>/, and entity classes in generated/schema.ts.
Store API
Entity.load(id): load entity (returns null if missing).new Entity(id): create new entity.entity.save(): write to store.
Recommended IDs
event.transaction.hashfor one entity per tx.event.transaction.hash.concatI32(event.logIndex.toI32())for one per log.Bytes.fromI32(dayID)for daily aggregates (e.g.event.block.timestamp.toI32() / 86400).
Contract bindings (eth_calls)
Bind and call read-only contract methods. Prefer emitting data in events to avoid eth_calls (they slow indexing). If needed, declare calls in the manifest (specVersion >= 1.2.0) so graph-node runs them in parallel before handlers:
eventHandlers:
- event: Transfer(...)
handler: handleTransfer
calls:
ERC20.poolInfo: ERC20[event.address].getPoolInfo(event.params.to)Dynamic data sources
In a mapping, instantiate a template to start indexing a new contract:
import { Exchange } from '../generated/templates'
export function handleNewExchange(event: NewExchange): void {
Exchange.create(event.params.exchange)
// Or with context:
let ctx = new DataSourceContext()
ctx.setString('tradingPair', event.params.tradingPair)
Exchange.createWithContext(event.params.exchange, ctx)
}Access context in the template mapping: dataSource.context().getString('tradingPair').
Key points
- All handler parameters and entity fields must be set before
save(); required schema fields must be non-null. - Use
@graphprotocol/graph-tsfor ByteArray, BigInt, BigDecimal, Address, store, and ethereum types.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/developing/creating/assemblyscript-mappings/
- https://thegraph.com/docs/en/subgraphs/developing/creating/graph-ts/
- https://github.com/graphprotocol/graph-tooling/tree/main/packages/ts
-->
The Graph — Subgraph Schema
The schema schema.graphql defines entities and relationships that mappings write to and the GraphQL API exposes.
Entities
- Every queryable type must have
@entity. Required field:id: Bytes!orid: String!(preferBytes!for performance). - Use
@entity(immutable: true)for entities that are never updated after creation; faster to write and query.
type Gravatar @entity(immutable: true) {
id: Bytes!
owner: Bytes
displayName: String
imageUrl: String
}Scalars
| Type | Use case |
|---|---|
| Bytes | Hashes, addresses |
| String | Text |
| Boolean | Flags |
| Int | 32-bit signed |
| Int8 | 64-bit signed (e.g. i64 from chain) |
| BigInt | uint32..uint256, int64..int256 |
| BigDecimal | High-precision decimals |
| Timestamp | i64 microseconds (timeseries) |
Relationships
- One-to-many: Store the reference on the "many" side; on the "one" side use
@derivedFrom(field: "otherEntity")so the reverse lookup is virtual and not stored. Storing the "many" side only is much more performant.
type Token @entity(immutable: true) {
id: Bytes!
tokenBalances: [TokenBalance!]! @derivedFrom(field: "token")
}
type TokenBalance @entity {
id: Bytes!
amount: Int!
token: Token!
}- Many-to-many: Prefer a mapping entity (e.g.
UserOrganization) with one row per pair and both sides using@derivedFrom.
Fulltext search
Add a _Schema_ type with @fulltext:
type _Schema_
@fulltext(
name: "bandSearch"
language: en
algorithm: rank
include: [{ entity: "Band", fields: [{ name: "name" }, { name: "description" }] }]
)
type Band @entity { id: Bytes! name: String! description: String! }Declare fullTextSearch under features in the manifest when using specVersion >= 0.0.4.
Key points
- Design entities around query needs and object relationships, not 1:1 with events.
- Use
Bytes!for ids when possible; avoid storing arrays of entities on the "many" side—use@derivedFrominstead.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/developing/creating/ql-schema/
-->
The Graph — Subgraph Manifest
The manifest subgraph.yaml defines which contracts and networks to index, which events/calls/blocks to react to, and how to map them to schema entities.
Structure
- schema:
file: ./schema.graphql - dataSources: one entry per contract; each has
source,mapping(abis, eventHandlers, callHandlers, blockHandlers), optionalcontext - templates: data sources without a fixed address (for factory/registry patterns)
Minimal data source
specVersion: 1.3.0
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum/contract
name: Gravity
network: mainnet
source:
address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC'
abi: Gravity
startBlock: 6175244 # optional; use contract creation block when possible
mapping:
kind: ethereum/events
apiVersion: 0.0.9
language: wasm/assemblyscript
entities:
- Gravatar
abis:
- name: Gravity
file: ./abis/Gravity.json
eventHandlers:
- event: NewGravatar(uint256,address,string,string)
handler: handleNewGravatar
file: ./src/mapping.tsHandlers
- eventHandlers:
event: <Signature>,handler: <functionName>. Optionaltopic1/topic0for filtering;receipt: truefor transaction receipt in handler. - callHandlers:
function: <signature>,handler: <functionName>. Only trigger on external calls; require Parity tracing (not supported on BNB, Arbitrum, etc.). - blockHandlers:
handler: <functionName>. Optionalfilter:kind: call(blocks with calls to this contract),kind: pollingwithevery: n, orkind: once(run once at start).
Indexer hints
indexerHints:
prune: auto # or "never" or a number of blocks to retainUse prune: never or a block count if you need time-travel queries or grafting.
Key points
- One subgraph can index multiple contracts (multiple entries in
dataSources) but not multiple networks. - List all entities written by this data source under
mapping.entities. - Use templates +
Template.create(address)orTemplate.createWithContext(address, context)in mappings for dynamically created contracts.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/developing/creating/subgraph-manifest/
- https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md
-->
The Graph — Data Source Templates
Use templates when the set of contracts to index is not known upfront (e.g. factory creates many child contracts). Define a normal data source for the parent and templates (no address under source) for each child type.
Manifest
dataSources:
- kind: ethereum/contract
name: Factory
network: mainnet
source:
address: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95'
abi: Factory
mapping:
file: ./src/mappings/factory.ts
# ... eventHandlers e.g. NewExchange(address,address)
templates:
- name: Exchange
kind: ethereum/contract
network: mainnet
source:
abi: Exchange
mapping:
file: ./src/mappings/exchange.ts
# ... eventHandlers for ExchangeInstantiating in mapping
When the factory emits an event (e.g. new exchange address), create a data source from the template:
import { Exchange } from '../generated/templates'
export function handleNewExchange(event: NewExchange): void {
Exchange.create(event.params.exchange)
}With context (e.g. trading pair from the event):
import { Exchange } from '../generated/templates'
export function handleNewExchange(event: NewExchange): void {
let context = new DataSourceContext()
context.setString('tradingPair', event.params.tradingPair)
Exchange.createWithContext(event.params.exchange, context)
}In the template mapping, read context:
import { dataSource } from '@graphprotocol/graph-ts'
let context = dataSource.context()
let tradingPair = context.getString('tradingPair')Key points
- A new data source only indexes from the block where it was created; it does not process prior blocks.
- For prior-block state, read contract state in the handler that creates the template and create entities representing that state.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/developing/creating/subgraph-manifest/#data-source-templates
-->
The Graph — Debug Forking
When a subgraph fails at block X on a remote Graph Node, debug forking lets you run a local Graph Node that fetches entity state from the remote subgraph's store up to block X. You can then deploy your fixed mappings locally starting at block X without waiting for a full sync.
Concept
- The remote node has the subgraph synced up to block X (and serves GraphQL from that store).
- You configure the local node with a fork-base URL such that
<fork-base>/<subgraph-id>is the GraphQL endpoint of that subgraph's store. - You deploy locally with --debug-fork <subgraph-id> so the local node lazily loads entities from the remote store instead of indexing from genesis.
- Set dataSources.source.startBlock in the manifest to the problematic block so indexing starts there and uses the forked state.
Deploy with forking
graph deploy <subgraph-name> --debug-fork <subgraph-id> --ipfs http://localhost:5001 --node http://localhost:8020- subgraph-id: The failing subgraph's ID (from Studio or the remote deployment).
- The local Graph Node must be started with the appropriate fork-base so it can resolve the subgraph-id to the remote GraphQL endpoint.
Workflow
1. Subgraph fails at block X on Studio or a remote node. 2. Start a local Graph Node with fork-base pointing at the remote API (e.g. Studio query URL base). 3. Set startBlock in the manifest to block X (or the block you want to debug from). 4. Fix the mapping and run graph build. 5. Deploy to the local node with --debug-fork <subgraph-id>. 6. Indexing runs from block X with state loaded from the remote store; reproduce the failure or verify the fix without full sync. 7. Iterate on steps 4–6 until fixed, then deploy to Studio/remote as usual.
Key points
- fork-base is the base URL; the node appends the subgraph-id to form the store endpoint.
- Use the subgraph ID (not deployment ID) for
--debug-forkwhen the remote serves that subgraph. - Speeds up debug cycles by avoiding re-sync from block 0.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/guides/subgraph-debug-forking/
-->
The Graph — Deployment and Publishing
Deploying pushes a subgraph to Subgraph Studio for testing; publishing makes it available on The Graph Network for curators and indexers.
Deploy vs publish
- Deploy: Push build to Studio (
graph deploy <SUBGRAPH_SLUG>). Get a development query URL; not onchain. Limit: 3 unpublished deployments per account. - Publish: From Studio (or
graph publishas of CLI 0.73.0), publish to the decentralized network. Subgraph becomes visible in Graph Explorer; curators can signal, indexers can index.
Prerequisites
- Graph CLI:
yarn global add @graphprotocol/graph-cliornpm install -g @graphprotocol/graph-cli - Subgraph created in Subgraph Studio; deploy key from the Subgraph details page
Deploy to Studio
1. Auth: graph auth <DEPLOY_KEY> (from Studio). 2. Init (if new): graph init <SUBGRAPH_SLUG> (slug from Studio). 3. Build: graph codegen && graph build. 4. Deploy: graph deploy <SUBGRAPH_SLUG>. Choose a version label (e.g. semver 0.0.1).
After deploy you get a Studio query URL (rate-limited, for testing). New deploys archive the previous Studio version; you can unarchive in Studio.
Publish to the network
- From Studio: Dashboard → Publish. Subgraph then appears in Graph Explorer.
- From CLI (0.73.0+):
graph codegen && graph buildthengraph publish. Optional flags:--protocol-network arbitrum-one|arbitrum-sepolia,--subgraph-id <value>,--ipfs <url>,--ipfs-hash <value>.
Metadata (name, description, etc.) can be updated in Studio without publishing a new version; publishing a new deployment creates a new version (costs and curation implications).
GRT signal
Adding GRT signal to a published subgraph incentivizes indexers. Eligibility: see feature support matrix and supported networks. Curators can signal on a specific version or use auto-migrate to follow the latest version. Developers can add signal when publishing from Studio or from Graph Explorer.
Key points
- Use supported networks for Network indexing; check supported networks and feature support matrix.
- Version labels in Studio/Explorer help curators choose which version to signal; prefer semver.
- Each account is limited to 3 deployed (unpublished) subgraphs; archive or publish to free a slot.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/developing/deploying/using-subgraph-studio/
- https://thegraph.com/docs/en/subgraphs/developing/publishing/publishing-a-subgraph/
-->
The Graph — Graph Node Developer Mode (gnd)
gnd is a developer-oriented Graph Node runner for local subgraph development. It simplifies setup (no IPFS required by default, automatic Postgres on Unix) and supports live redeployment when the build changes.
Prerequisites
- Subgraph that builds with
graph build - PostgreSQL installed and running (or let gnd manage it on Unix)
- Ethereum RPC endpoint (e.g. local Anvil, Hardhat, or remote)
Install
npm i @graphprotocol/graph-cli
graph node installThis installs the gnd binary (Unix: ~/.local/bin, Windows: %USERPROFILE%\gnd\bin). Ensure that directory is in your PATH.
Run
From the subgraph project directory:
Unix (minimal):
gnd --ethereum-rpc mainnet:http://localhost:<PORT>Unix with hot-reload (redeploy when build changes):
gnd --ethereum-rpc mainnet:http://localhost:<PORT> --watchWindows (Postgres URL required):
gnd --ethereum-rpc mainnet:http://localhost:<PORT> --postgres-url "postgresql://graph:yourpassword@localhost:5432/graph-node"Query endpoint
After deploy, query at:
http://localhost:8000/subgraphs/name/subgraph-0/(Exact path may depend on deploy name; check gnd output.)
Options
| Flag | Description |
|---|---|
--ethereum-rpc | network[:capabilities]:URL. Required. |
--postgres-url | PostgreSQL connection URL. Required on Windows; optional on Unix (auto temporary DB in --database-dir). |
--watch | Watch build directory and redeploy on changes. |
--manifests | Path(s) to manifest. Default: ./subgraph.yaml. |
--database-dir | Directory for temporary Postgres (Unix). Default: ./build. |
--ipfs | IPFS endpoint(s). Default: https://api.thegraph.com/ipfs. |
Key points
- On Unix, omit
--postgres-urlto use an automatic temporary Postgres instance. - Use
--watchfor a faster feedback loop when editing mappings. - IPFS defaults to The Graph’s public IPFS; override if you use a local node.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/developing/creating/graph-node-dev/
-->
The Graph — Querying from an Application
Subgraphs are queried via GraphQL. Use the correct endpoint and secure API key handling when building apps.
Endpoints
- Subgraph Studio (testing only, rate-limited):
https://api.studio.thegraph.com/query/<ID>/<SUBGRAPH_NAME>/<VERSION>
- The Graph Network (production):
https://gateway.thegraph.com/api/<API_KEY>/subgraphs/id/<SUBGRAPH_ID>
Use the Network endpoint with an API key for production; pass the key in the URL path or as Authorization: Bearer <API_KEY>.
API keys
- Create and manage keys in Subgraph Studio → API Keys.
- Store keys in environment variables or a secrets manager; do not hardcode or expose in client-side code.
- Optional: set spending limits, restrict domains, limit which subgraphs the key can query.
Graph Client
The Graph's graph-client (@graphprotocol/client-cli) supports cross-chain subgraph queries, block tracking, and auto-pagination. Integrates with Apollo, URQL, React Query.
1. Install: yarn add -D @graphprotocol/client-cli. 2. Define queries in .graphql files. 3. Configure .graphclientrc.yml with sources (each with handler.graphql.endpoint) and documents. 4. Run graphclient build to generate typed documents. 5. Use generated types and execute(ExampleQueryDocument, {}) in app code.
Apollo and URQL
Use the Network URL as uri/url; pass variables for parameters. Prefer graph-client when you need cross-subgraph queries or typed generation.
Key points
- Prefer the Network endpoint and API key in URL or Bearer header for production.
- Restrict API keys by domain and subgraph in Studio to limit exposure.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/querying/from-an-application/
- https://thegraph.com/docs/en/subgraphs/querying/managing-api-keys/
-->
The Graph — Subgraph Composition
Subgraph composition lets a dependent subgraph use one or more source subgraphs as data sources, merging their entities so you can build on existing indexed data without re-indexing chains.
Overview
- Source subgraphs: Standard subgraphs that index chain data and expose entities.
- Dependent (composed) subgraph: Declares source subgraphs as data sources; its mappings react to entity changes from those sources (e.g. new/updated entities) and can create new entities. Combines data from up to 5 source subgraphs into one API.
Benefits: reuse and mix existing data, faster development, fewer duplicate indexing jobs. All source subgraphs must be deployed before deploying the composed subgraph.
Prerequisites
Source subgraphs
- specVersion 1.3.0 or later (see graph-node v0.37.0).
- Immutable entities only: Only immutable entities can be composed. Pruning is allowed on sources, but only immutable entities are visible to the dependent subgraph.
- Source subgraphs cannot use grafting on the entities that are composed.
- Aggregated entities can be composed; entities built on top of them cannot perform additional aggregations.
Composed subgraph
- Maximum 5 source subgraphs.
- Same chain: All data sources must be from the same chain.
- No nested composition: You cannot use a composed subgraph as a source of another composed subgraph.
- No mixing: You cannot combine an onchain data source (event/call/block handlers) with a subgraph data source in the same composed subgraph—composed subgraphs use only subgraph data sources.
Workflow
1. Deploy each source subgraph (each indexes its contracts and publishes entities). 2. In the dependent subgraph manifest, add data sources that reference the deployment IDs of those source subgraphs (not Subgraph IDs). 3. In the dependent schema, import or extend the source schemas and add any new entities/fields. 4. Write mappings that react to source entities (e.g. block handlers or entity triggers, depending on the API). 5. Build and deploy the composed subgraph. Update the manifest when a source’s deployment ID changes (e.g. after a new publish).
Key points
- Use Deployment ID in the dependent manifest for each source subgraph; redeploying a source creates a new deployment ID.
- Keep source subgraphs immutable-entity-only where they are composed; avoid grafting on those entities.
- Example repo: example-composable-subgraph.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/guides/subgraph-composition/
- https://github.com/graphprotocol/graph-node/releases/tag/v0.37.0
-->
The Graph — Subgraph Linter
Subgraph Linter is a static analysis tool that checks mapping code for patterns that often cause runtime crashes, bad entity state, or poor performance. Run it locally or in CI; use the VS Code extension for inline diagnostics.
When to use
Run the linter to catch issues before indexing, such as:
- Entities saved with missing required fields
- Entity overwrites (stale instance saved after a helper updated the same entity)
- Optional values force-unwrapped without null checks
- Division by zero (denominator not guarded)
@derivedFromfields mutated or left inconsistent- Contract calls (eth_calls) used in handlers but not declared in the manifest
calls:block
CLI
Build and run against your manifest:
cd subgraph-linter && npm install && npm run build
npm run check -- --manifest ../your-subgraph/subgraph.yamlOptional: --tsconfig ../your-subgraph/tsconfig.json, --config ./subgraph-linter.config.json.
Configuration
Use subgraph-linter.config.json (or --config) for:
- Severity overrides: Turn specific checks into
errororwarning(onlyerrorfails the run). - Suppression:
allowWarnings/allowErrorsto control exit code.
Example:
{
"severityOverrides": {
"division-guard": "error",
"undeclared-eth-call": "warning"
}
}Inline suppression: Add // [allow(check-id)] or // [allow(all)] on the line to silence a diagnostic when you know the pattern is safe.
VS Code
The Subgraph Linter extension discovers subgraph.yaml (excluding build/dist), runs on save by default, and shows results in the editor and Problems panel.
- Commands: "Subgraph Linter: Run Analysis", "Subgraph Linter: Add Call Declaration" (quick fix for undeclared eth_calls).
- Settings (prefix
subgraphLinter):manifestPaths,tsconfigPath,configPath,runOnSave.
Key checks
| Check | Purpose |
|---|---|
| entity-overwrite | Stale entity saved after a helper loaded/saved the same entity |
| unexpected-null | Required fields not set before save, or @derivedFrom assigned |
| unchecked-load | Entity.load() used as non-null without handling null |
| division-guard | Division where denominator can be zero |
| derived-field-guard | Base fields updated but derived fields not recomputed before save |
| helper-return-contract | Helper returns entity with unset required fields; call site saves it |
| undeclared-eth-call | Handler (or helper) makes contract call not listed in manifest calls: |
Key points
- Use the linter alongside unit tests; it does not replace runtime or integration tests.
- Declare all eth_calls in the manifest so graph-node can run them in parallel and cache; the linter helps find missing declarations.
- Repo: graphprotocol/subgraph-linter.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/guides/subgraph-linter/
- https://github.com/graphprotocol/subgraph-linter
-->
The Graph — Substreams Sinks
Sinks consume Substreams output and send it to a destination (database, file, PubSub, etc.). Choose a sink that fits your stack and hosting needs.
Overview
After building a Substreams package, you run a sink that subscribes to the Substreams output and writes data. Sinks are either officially supported (StreamingFast, Pinax) or community-maintained.
Official sinks
| Sink | Maintainer | Purpose |
|---|---|---|
| SQL | StreamingFast | Persist to SQL database |
| Go SDK | StreamingFast | Build custom sinks in Go |
| Rust SDK | StreamingFast | Build custom sinks in Rust |
| JS SDK | StreamingFast | Consume from JavaScript/Node |
| KV Store | StreamingFast | Key-value store |
| PubSub | StreamingFast | Publish to a PubSub topic |
| Prometheus | Pinax | Metrics |
| Webhook | Pinax | HTTP webhooks |
| CSV | Pinax | File output |
Repos: substreams-sink-sql, substreams-sink (Go), substreams-sink-rust, substreams-js, substreams-sink-kv, substreams-sink-pubsub, substreams-sink-prometheus, substreams-sink-webhook, substreams-sink-csv.
Community sinks
Examples: MongoDB, Files, KV Store, Prometheus (community forks). Support is community-driven. See Substreams docs – Community Sinks.
Choosing a sink
- SQL: When you need queryable persistence in a relational DB.
- Direct streaming / JS SDK: When your app consumes data in real time (e.g. Node/TypeScript).
- PubSub: When you want to fan out to other services (event-driven pipelines).
- KV: When you need a simple key-value view of the output.
Hosted sink options (e.g. SQL or PubSub managed by StreamingFast) may be available; contact the team for offerings.
Key points
- Use official sinks when you need active support; community sinks for flexibility.
- Sink repos live in different orgs (streamingfast, pinax-network, community); check docs.substreams.dev for the current list and how-to guides.
<!-- Source references:
- https://thegraph.com/docs/en/substreams/developing/sinks/
- https://docs.substreams.dev/how-to-guides/sinks/
-->
The Graph — Substreams
Substreams is a parallel blockchain indexing stack used to speed up subgraph indexing and support non-EVM chains and multiple sinks.
Overview
- Input: Blockchain data (blocks, traces, account changes).
- Program: Rust code that defines transformations; compiled to WASM.
- Execution: Substreams provider feeds data to the WASM module; transformations run in parallel.
- Sinks: Subgraph, Postgres, Clickhouse, Mongo, etc.
Benefits
- Faster indexing via parallelized engine.
- Multi-chain: EVM, Solana, Injective, Starknet, Vara.
- Richer data: trace-level on EVM, account changes on Solana; fork/disconnect handling.
Typical flow
1. Write Rust modules that map blocks/streams to your output types. 2. Build WASM: one CLI command. 3. Send WASM to a Substreams endpoint; provider runs it and can write to a chosen sink (e.g. subgraph).
Example (conceptual): extract block number/hash/parent from an Ethereum block in Rust, output a custom type; package as WASM and run via Substreams.
Key points
- Substreams docs and registry are maintained by StreamingFast: https://docs.substreams.dev.
- Use for high-throughput or non-EVM indexing; subgraphs remain the primary query interface for many apps.
<!-- Source references:
- https://thegraph.com/docs/en/substreams/introduction/
- https://docs.substreams.dev
-->
The Graph — Unit Testing with Matchstick
Matchstick is a unit testing framework for subgraph mappings. Tests run in a sandbox; you mock events/calls and assert on store state.
Setup
yarn add --dev matchstick-asAdd a test script: "test": "graph test". PostgreSQL is required (or use Docker with graph test -d).
Running tests
graph test # all tests
graph test gravity # tests in gravity.test.ts or gravity/
graph test path/to/file.test.ts
graph test -d # run in Docker
graph test -c # coverage mode
graph test -r # force recompileTest structure (matchstick-as >= 0.5.0)
- describe(name, () => {}) — group tests.
- test(name, () => {}, should_fail?: bool) — single test.
- beforeAll / afterAll — run once per file or describe.
- beforeEach / afterEach — use clearStore() to reset store between tests when needed.
Assertions and mocks
- assert.fieldEquals(entityType, id, fieldName, value) — assert one field.
- assert.entityCount(n) — assert number of entities.
- createMockedFunction(...) — mock contract calls (eth_calls).
- newMockEvent() — build mock events.
- dataSourceMock — mock context(), address(), network() for dynamic data source tests.
Handlers must be exported from the test file. Use create*Event helpers to build event instances.
Key points
- Clear store between tests (beforeEach + clearStore) when they share entities.
- Mock contract calls with createMockedFunction when mappings use eth_calls.
- Keep graph-ts and matchstick-as versions aligned.
<!-- Source references:
- https://thegraph.com/docs/en/subgraphs/developing/creating/unit-testing-framework/
-->