Membangun lapisan data dApp: arsitektur backend off-chain, indexing event dengan The Graph, subgraph dan entity, plus praktik membuat subgraph lengkap untuk aplikasi kalian

Setelah di episode 15 kalian merancang tokenisasi RWA, pada episode ini kita membangun infrastruktur yang membuat data on-chain bisa dibaca: backend & indexing. Blockchain menyimpan semua data — tetapi tidak dirancang untuk query yang kompleks. Menelusuri "semua transfer token X oleh akun Y" dengan memindai block satu per satu sangat lambat dan mahal.
Di sinilah The Graph berperan: ia mengindeks event on-chain dan menyediakan API GraphQL yang cepat. Di episode ini kita juga melihat posisi The Graph dalam arsitektur backend keseluruhan.
dApp 2026 tidak hanya kontrak + frontend. Ada lapisan off-chain yang memegang data non-kripto:
Backend service menyimpan metadata, user profile, preferensi, dan logika yang tidak perlu (atau tidak boleh) on-chain. Data kripto (saldo, transfer, kepemilikan) diambil dari subgraph. Memisahkan keduanya adalah keputusan arsitektur yang sehat.
The Graph adalah protokol indexing terdesentralisasi. Satu dApp mendefinisikan subgraph — file YAML + skema GraphQL + mapping script yang menentukan: event apa yang diindeks, bagaimana mengubahnya menjadi entity, dan bagaimana mengeksposnya via API GraphQL.
Alur kerja subgraph:
Kontrak → event → mapping (AssemblyScript) → entity → API GraphQLKomponen subgraph:
| Komponen | Peran |
|---|---|
subgraph.yaml | Mendeklarasikan sumber data & event |
schema.graphql | Mendefinisikan tipe entity |
mapping.ts | Logika yang mengubah event menjadi entity |
graph-cli | Generate, deploy, dan query subgraph |
Mari buat subgraph untuk kontrak PaymentReceiver dari episode 14. Pertama, install tooling dan inisialisasi:
npm install -g @graphprotocol/graph-cli
graph init --studio payment-subgraphDeklarasikan sumber data di subgraph.yaml:
specVersion: 1.0.0
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum/contract
name: PaymentReceiver
network: sepolia
source:
address: "0xPAYMENT_CONTRACT"
abi: PaymentReceiver
startBlock: 7000000
mapping:
kind: ethereum/events
apiVersion: 0.0.7
language: wasm/assemblyscript
entities:
- Order
abis:
- name: PaymentReceiver
file: ./abis/PaymentReceiver.json
eventHandlers:
- event: PaymentReceived(bytes32 indexed orderId, address indexed token, address indexed payer, uint256 amount)
handler: handlePaymentReceived
file: ./src/payment-receiver.tsDefinisikan entity di schema.graphql:
type Order @entity {
id: ID!
token: Bytes!
payer: Bytes!
amount: BigInt!
blockNumber: BigInt!
timestamp: BigInt!
}Tulis mapping di src/payment-receiver.ts:
import { PaymentReceived } from "../generated/PaymentReceiver/PaymentReceiver";
import { Order } from "../generated/schema";
export function handlePaymentReceived(event: PaymentReceived): void {
const order = new Order(event.params.orderId.toHexString());
order.token = event.params.token;
order.payer = event.params.payer;
order.amount = event.params.amount;
order.blockNumber = event.block.number;
order.timestamp = event.block.timestamp;
order.save();
}Build dan deploy:
npm run build
graph deploy --studio payment-subgraphSetelah deploy, frontend kalian bisa query dengan GraphQL:
{
orders(first: 10, orderBy: timestamp, orderDirection: desc) {
id
payer
amount
token
}
}Perhatikan orderId menjadi ID entity — entity harus memiliki ID unik. Menggunakan toHexString() memastikan bytes32 bisa menjadi ID GraphQL yang valid.
Note
The Graph adalah pilihan populer, tapi bukan satu-satunya. Alternatif 2026: Ponder (indexing cepat berbasis Postgres untuk tim yang ingin kontrol penuh), Envio, dan framework Subquery. Untuk data kompleks dengan relasi banyak-ke-banyak, database relasional off-chain (Ponder + Postgres) sering lebih fleksibel. Pilih berdasarkan kebutuhan: siap pakai vs kontrol.
Beberapa praktik yang menyelamatkan waktu production:
startBlock yang tepat: mulai index dari block deploy kontrak, bukan block genesis — menghemat resource besar.orderId, tokenId, atau hash kombinasi — hindari counter global.block terbaru; untuk indexing mandiri, kalian perlu mekanisme rollback sendiri.Inti yang harus dibawa pulang:
startBlock yang tepat dan entity ID deterministik menghemat resource.Di episode 17 selanjutnya kita masuk ranah efisiensi — Gas Optimization: pola hemat gas, calldata, storage layout, dan praktik mengoptimasi kontrak. Mari lanjut ke episode 17!