Defining the Schema
The schema.graphql
file serves as a representation of your application's data model. It defines entity types that directly correspond to database tables, and the event handlers you create are responsible for creating and updating records within those tables. Additionally, the GraphQL API is automatically generated based on the entity types specified in the schema.graphql
file, to allow access to the indexed data.
Entity types are identified with the directive within the schema.graphql
file.
Example schema from the Greeter template:
type User {
id: ID!
greetings: [String!]!
latestGreeting: String!
numberOfGreetings: Int!
}
Every entity type must include an id
field that is of type ID!
, String!
, Int!
, Bytes!
, or BigInt!
. The id
field serves as a unique identifier for each instance of the entity.
Enums
The schema file also supports the use of enum types. An example of an enum definition and usage within the schema is shown below:
enum AccountType {
ADMIN
USER
}
type User {
id: ID!
balance: Int!
accountType: AccountType!
}
Enum types are generated as string union types for TypeScript and JavaScript and as polymorphic variants for ReScript. Therefore to set an enum field in an entity in TypeScript and JavaScript, the string of the enum value is used:
import { AccountType } from "../generated/src/Enums.gen";
let accountType: AccountType = "USER";
let user = {
id: event.params.id,
balance: event.params.balance,
accountType,
};
For ReScript, we use the polymorphic variant
let accountType: Enums.accountType = #USER;
let user: Types.userEntity = {
id: event.params.id,
balance: event.params.balance,
accountType
};
Scalar Types
In GraphQL, scalars represent fundamental data types such as strings and numbers. Each GraphQL scalar is mapped to a corresponding JavaScript, TypeScript, or ReScript type, which is used in event handler code, depending on the language chosen. The following table provides an overview of the available scalar types, along with their associated JavaScript, TypeScript, and ReScript types:
Name | Description | JavaScript/TypeScript Type | ReScript Type |
---|---|---|---|
ID | A unique identifier field | string | string |
String | A UTF-8 character sequence | string | string |
Int | A signed 32-bit integer | number | int |
Float | A signed floating-point value | number | float |
Boolean | Represents a true or false value | boolean | bool |
Bytes | A UTF-8 character sequence with a 0x prefix | string | string |
BigInt | A signed integer (equivalent to solidity int256) | bigint | bigint |
BigDecimal | An arbitrary size floating point number | BigDecimal (imported from "generated") | BigDecimal.t |
Timestamp | Timestamp with time zone | Date | Js.Date.t |
You can find out more on GraphQL here.
Once you have set up your config and schema file you can run pnpm codegen
to generate the functions that you will use in your handlers.
pnpm codegen
Defining One-to-Many Relationships
type NftCollection {
id: ID!
contractAddress: Bytes!
name: String!
symbol: String!
maxSupply: BigInt!
currentSupply: Int!
tokens: [Token!]! @derivedFrom(field: "collection")
}
type Token {
id: ID!
tokenId: BigInt!
collection: NftCollection!
owner: User!
}
Assume that each NftCollection
can have multiple Token
objects. This is represented by the [Token!]
in NftCollection
definition, where the field's type is set to another entity type.
When you create a Token
entity, the value of the collection
field is set to the id
of its associated NftCollection
entity.
Note that in the NftCollection
schema, the tokens
field can't be directly accessed or modified. Fields marked with the @derivedFrom
directive function are virtual fields and are only accessible when executing GraphQL API queries. This is commonly known as reverse lookup, as the relationship is established on the "many" end of the association.
Adding Indexes to Fields
Add an index to a field to improve read performance and enable field queries in your loaders.
Indices will automatically be added to all entity id
fields and all fields referenced using @derivedFrom
directive.
Example
type Token {
id: ID!
tokenId: BigInt!
collection: NftCollection!
owner: User! @index
}
Advanced: @config Directive for Decimal Precision of BigInt
and BigDecimal
Fields
When working with large integers or high-precision decimal numbers in your application, you might need to customize the precision and scale of your BigInt
and BigDecimal
fields. This ensures that your database stores these numbers accurately according to your specific requirements. If you know your numbers will not be too big, you can also optimize the database by not over-allocating on the precision.
Using the @config
Directive
The @config
directive allows you to specify custom configurations for fields. For BigInt
and BigDecimal
types, you can define precision and scale parameters.
Syntax for BigInt
:
fieldName: BigInt @config(precision: <number_of_digits>)
Syntax for BigDecimal
:
fieldName: BigDecimal @config(precision: <total_digits>, scale: <decimal_digits>)
Example:
type Entity {
id: ID!
amount: BigInt @config(precision: 76)
price: BigDecimal @config(precision: 10, scale: 2)
}
In this example:
- The
amount
field is aBigInt
with up to 76 digits. - The
price
field is aBigDecimal
with a total of 10 digits and 2 decimal places.