Skip to main content
Version: Firesquid

Store interface

Store is a generic interface exposed by XXXContext.store to the handlers. The concrete type is inferred from the Database argument of the BatchProcessor.run method:

run<Store>(db: Database<Store>, handler: (ctx: BatchContext<Store, Item>) => Promise<void>): void

The Database interface only defines the logic of how the processor persists the processor status:

export interface Database<S> {
// initialize the connection and run migrations
connect(): Promise<number>
// run handlers for blocks in the range `[from, to]` in a single transaction
transact(from: number, to: number, cb: (store: S) => Promise<void>): Promise<void>
// update and persist the processor status to `height`
advance(height: number): Promise<void>
}

Typeorm Databases

The Subsquid SDK offers two implementations of Database offering a TypeORM-compatible Store in the @subsquid/typeorm-store package: FullTypeormDatabase and TypeOrmDatabase.

TypeormDatabase provides a ctx.store, which is

  • Lazy (no transaction is opened if no data is read or written to the store). This is useful e.g. when one want to subscribe for some frequent events but only interested in the cases when the events were emitted under specific conditions (e.g. by a specific contract)
  • Looks like a stripped down version of TypeORM EntityManager (no .query(), no cascading saves)
  • The schema name and the transaction isolation level can be passed as an optional constructor argument
  • Optimized performance for persisting large arrays of entities via store.save and store.insert

The performance improvements come from the fact that Store implements .save() via upsert and all the data manipulation methods are translated into a single SQL statement (compared to multiple SQL statements generated by EntityManager)

Usage:

import { Store, TypeormDatabase } from '@subsquid/typeorm-store'

processor.run(new TypeormDatabase(), async ctx => {
// ...
await ctx.store.save([new FooEntity({ id: 1}), new FooEntity({ id: 2})])
})

In the snippet above, ctx.store passed to the handlers will be of type Store.

FullTypeormDatabase

FullTypeormDatabase provides a ctx.store compatible with EntityManager, albeit without .get() method

Usage:

import { FullTypeormDatabase } from '@subsquid/typeorm-store'
import { EntityManager } from 'typeorm'

processor.run(new FullTypeormDatabase(), async ctx => {
// ...
await ctx.store.save(new FooEntity({ id: 1}))
})

In the snippet above, ctx.store passed to the handlers will be of type EntityManager.

Custom Database

A custom implementation of the Database interface is a recommended solution for squids with multiple data sinks and/or for non-transactional or non-relational databases. In such a case, the inferred Store facade exposed to the handlers may provide multiple targets for persisting the data. Database.transact should handle potential edge-cases when writes to either of the data sinks fail.

Reference implementations will be provided in due course.