Joystream query node can be generated by using substrate-query-node/cli
.
Cli create a folder named generated
and put everthing inside it.
$ cli codegen
Start graphql server:
$ cd generated/graphql-server
$ yarn start:dev
Start block indexer:
$ cd generated/indexer
$ yarn start
Every mapping function get a parameter of DB
type
import { DB } from '../generated/indexer';
db
object is for database operations save/get/remove and access to event itself
Define the event handler function with the following signature and import the entity class
import { MemberRegistereds } from '../generated/indexer/entities/MemberRegistereds';
export async function handleMemberRegistered(db: DB) {}
Inside the handler function create a new instance of the entity and fill properties with event data.
// Get event data
const { AccountId, MemberId } = db.event.event_params;
const member = new MemberRegistereds({ accountId: AccountId.toString(), memberId: +MemberId });
Call db.save()
method to save data on database
// Save to database.
db.save<MemberRegistereds>(member);
Query database
// Query from database
const findOptions = { where: { memberId: 123 } }; // match the record
const m = await db.get(MemberRegistereds, findOptions);
Below you can find the complete code
Complete code
import { MemberRegistereds } from "../generated/indexer/entities/MemberRegistereds";
import { DB } from "../generated/indexer";
export async function handleMemberRegistered(db: DB) {
// Get event data
const { AccountId, MemberId } = db.event.event_params;
const member = new MemberRegistereds({ accountId: AccountId.toString(), memberId: +MemberId };
// Save to database.
db.save<MemberRegistereds>(member);
// Query from database
const m = await db.get(MemberRegistereds, { where: { memberId: 123 } });
}
schema.graphql
is where you define types for graphql server. Graphql server use these types to generate db models, db tables, graphql resolvers.Below you can find a type defination example:
type Membership {
# Member's root account id
accountId: String!
# Member's id
memberId: Int!
# The unique handle chosen by member
handle: String
# A Url to member's Avatar image
avatarUri: String
# Short text chosen by member to share information about themselves
about: String
}
Important Relationship between types not supported yet!
Block indexer is block consumer and every block can have events that we want to store their data. So indexing data from events we need to send the event to a function or a class that can handle the event and stores the event data on the database. mappings
are the functions that we use to update our database with events data. Functions that we define in our mappings will be called only when the event name match our function name (function name pattern is 'handle' + eventName
). We call mapping functions as event handlers. Each event handler have only one parameter which is the db: DB
. Every database operation is made with db
object and the event can be accessed with db
object. Below you can find an example for the event a handler:
// mappings/index.ts
import { DB } from '../generated/indexer';
export function handleMemberRegistered(db: DB) {
console.log(`Event parameters: ${db.event.event_params}`);
}
Block indexer connects to a blockchain node via WebSocket so we need to tell block indexer where to find the address of the node. Also, on the initialization of the indexer, we must pass the type register function as a parameter. So we put these variables inside the .env
file that indexer can find and use them. For Joystream we will be running a local development node and add the name of the function, the package for the type registration:
WS_PROVIDER_ENDPOINT_URI=ws://localhost:9944
TYPE_REGISTER_PACKAGE_NAME=@joystream/types
TYPE_REGISTER_FUNCTION=registerJoystreamTypes
Database connections options are defined in .env
:
DB_NAME=test
DB_USER=postgres
DB_PASS=postgres
DB_HOST=localhost
DB_PORT=5432
GRAPHQL_SERVER_PORT=4000