The Graph subgraph development: design, deployment, and optimization

We often encounter a problem: smart contracts don't store state history in a query-friendly way. `eth_getLogs` with event filtering is a blunt tool — no sorting, no aggregation, no relationships between events across different contracts. As a result, the frontend either pulls tons of data and proces

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1441
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    998
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1267
  • image_logo-advance_0.webp
    B2B Advance company logo design
    713
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1003

We often encounter a problem: smart contracts don't store state history in a query-friendly way. eth_getLogs with event filtering is a blunt tool — no sorting, no aggregation, no relationships between events across different contracts. As a result, the frontend either pulls tons of data and processes it client-side, or the team spins up its own indexing backend. The Graph solves this in a standard way: you describe what to index, and the network does it for you. Our team has developed over 20 subgraphs for DeFi protocols and NFT marketplaces, accumulating experience in optimization and debugging. Contact us for a consultation on your project — we'll help design the schema and choose the stack.

A subgraph is essentially a declaration: which contracts to listen to, which events to process, how to transform data into entities. Writing it correctly the first time is harder than it seems.

Optimizing schema for GraphQL queries

The schema should be designed based on what queries the frontend needs — not on the structure of contract events. A typical mistake: making entities one-to-one with events, which leads to N+1 queries on the frontend. Denormalized entities with pre-aggregated data reduce query count by 3–5 times.

The right approach — denormalized entities with pre-aggregated data:

type Pool @entity { id: ID! # pool address token0: Token! token1: Token! feeTier: BigInt! totalVolumeUSD: BigDecimal! # cumulative volume — updated on each Swap totalValueLockedUSD: BigDecimal! txCount: BigInt! swaps: [Swap!]! @derivedFrom(field: "pool") } type Swap @entity { id: ID! # txHash + logIndex pool: Pool! sender: Bytes! recipient: Bytes! amount0: BigDecimal! amount1: BigDecimal! amountUSD: BigDecimal! timestamp: BigInt! blockNumber: BigInt! } 

@derivedFrom is a virtual relationship — it doesn't store an array of IDs in the Pool record. This is important for performance: a pool with thousands of swaps won't grow in record size. Example query the frontend can run:

{ pools(first: 10) { id totalVolumeUSD swaps(first: 5) { amountUSD timestamp } } } 

Why AssemblyScript is dangerous for TypeScript developers?

AssemblyScript is a strictly typed language that compiles to WebAssembly. TypeScript habits are dangerous here:

// WRONG — null reference in AS causes panic let pool = Pool.load(event.address.toHexString()) pool.txCount = pool.txCount.plus(BigInt.fromI32(1)) // pool may be null // RIGHT let poolId = event.address.toHexString() let pool = Pool.load(poolId) if (pool === null) { pool = new Pool(poolId) pool.txCount = BigInt.fromI32(0) pool.totalVolumeUSD = BigDecimal.fromString("0") } pool.txCount = pool.txCount.plus(BigInt.fromI32(1)) pool.save() 

BigDecimal for financial values is mandatory. BigInt from the contract must be converted considering token decimals:

function convertTokenToDecimal(tokenAmount: BigInt, exchangeDecimals: BigInt): BigDecimal { if (exchangeDecimals == BigInt.fromI32(0)) { return tokenAmount.toBigDecimal() } return tokenAmount.toBigDecimal().div( BigInt.fromI32(10).pow(exchangeDecimals.toI32() as u8).toBigDecimal() ) } 

How to debug slow synchronization?

If a subgraph syncs slower than expected, run through this checklist:

  1. Count the number of callHandlers — replace with eventHandlers where possible. eventHandlers are 5–10 times faster.
  2. Ensure startBlock is not too early. Ideally, it's the deployment block of the contract.
  3. Check the number of eth_call in handlers — each contract call from a mapping adds an RPC request.
  4. Use ipfs.cat minimally — it's a slow operation.
Handler type Speed Usage
eventHandlers Fast (2000–5000 blocks/min) Any events emitted by the contract
callHandlers Slow (5–10 times slower) If the contract doesn't emit events
blockHandlers Very slow Only when no alternative, with filter: { kind: once }

Typical mistakes in handlers:

  • Forgetting to check for null before Pool.load
  • Setting startBlock = 0
  • Using callHandlers instead of eventHandlers where the opposite is possible
  • Not converting BigInt to BigDecimal with decimals accounted for

How to choose between Hosted Service and Decentralized Network?

For production protocols, we recommend the decentralized network: it provides censorship resistance and resistance to shutdown. Hosted Service is free but only suitable for development and testing. Comparison:

Hosted Service Decentralized Network
Cost Free (service shutting down) GRT tokens (Indexer fees)
Latency Low Higher (~100–500ms)
Censorship resistance No (centralized) Yes
SLA No guarantees Depends on Indexers
Suitable for Development, testing Production with decentralization requirement

For deployment to the decentralized network, use Graph Studio:

graph auth --studio <deploy-key> graph codegen && graph build graph deploy --studio <subgraph-name> 

More about the architecture can be found in the The Graph documentation. Contact us for consultation on network selection and schema optimization.

Subgraph development process: stages and timelines

We work according to the following plan:

  1. Analysis of contract ABIs and identification of events and calls to index.
  2. GraphQL schema design tailored to frontend queries (with emphasis on denormalization).
  3. Writing AssemblyScript handlers with null handling, BigInt conversion, and performance optimization.
  4. Local testing using graph-cli and debugging slow spots.
  5. Deployment to the chosen network and synchronization monitoring setup.

Timelines depend on contract complexity and number of entities: from 3 to 10 working days. Cost is calculated individually after analyzing your project.

What is included in our subgraph development work

  • Analysis of contract ABIs and identification of needed events/calls
  • Schema design for specific frontend queries
  • Writing and testing AssemblyScript handlers
  • Performance optimization (saving up to 40% on RPC calls through denormalization)
  • Deployment and synchronization monitoring
  • Documentation of GraphQL endpoints and example queries

Our team has extensive experience in blockchain development, with over 30 successful projects on Ethereum, Polygon, BNB Chain, Solana. Order subgraph development from professionals and get fast indexing without compromises.