Schema API
The ponder.schema.ts
file defines your database tables and their relationships. Tables defined in this file are used to store indexed blockchain data and are automatically exposed via the GraphQL API.
File requirements
The ponder.schema.ts
must use named exports for tables, enums, and relations, and these objects must be created using the corresponding functions exported by @ponder/core
.
import { onchainTable } from "@ponder/core";
export const pets = onchainTable("pets", (t) => ({
name: t.text().primaryKey(),
age: t.integer().notNull(),
}));
onchainTable
The onchainTable
function accepts three positional arguments.
Argument | Type | Description |
---|---|---|
name | string | The SQL table name. Use snake_case . |
columns | (t: TableBuilder) => Record<string, Column> | A function that returns column definitions. |
constraints? | (table: Table) => Record<string, Constraint> | Optional function that returns table constraints like composite primary keys and indexes. |
import { onchainTable } from "@ponder/core";
export const transferEvents = onchainTable(
"transfer_event", // SQL table name
(t) => ({ // Column definitions
id: t.text().primaryKey(),
from: t.hex().notNull(),
to: t.hex().notNull(),
value: t.bigint().notNull(),
}),
(table) => ({ // Constraints & indexes
fromIdx: index().on(table.from),
})
);
Column types
The schema definition API supports most PostgreSQL data types. Here's a quick reference for the most commonly used data types. For a complete list, see the Drizzle documentation.
Name | Description | TypeScript type | SQL data type |
---|---|---|---|
text | UTFâ8 character sequence | string | TEXT |
integer | Signed 4âbyte integer | number | INTEGER |
real | Signed 4-byte floatingâpoint value | number | REAL |
boolean | true or false | boolean | BOOLEAN |
timestamp | Date and time value (no time zone) | Date | TIMESTAMP |
json | JSON object | any or custom | JSON |
bigint | Large integer (holds uint256 and int256 ) | bigint | NUMERIC(78,0) |
hex | UTFâ8 character sequence with 0x prefix | 0x${string} | TEXT |
Column modifiers
Column modifiers can be chained after column type definitions.
Modifier | Description |
---|---|
.primaryKey() | Marks column as the table's primary key |
.notNull() | Marks column as NOT NULL |
.array() | Marks column as an array type |
.default(value) | Sets a default value for column |
.$default(() => value) | Sets a dynamic default via function |
.$type<T>() | Annotates column with a custom TypeScript type |
Constraints
Primary key
Every table must have exactly one primary key defined using either the .primaryKey()
column modifier or the primaryKey()
function in the table constraints argument.
import { onchainTable, primaryKey } from "@ponder/core";
// Single column primary key
export const tokens = onchainTable("tokens", (t) => ({
id: t.bigint().primaryKey(),
}));
// Composite primary key
export const poolStates = onchainTable(
"pool_states",
(t) => ({
poolId: t.bigint().notNull(),
address: t.hex().notNull(),
}),
(table) => ({
pk: primaryKey({ columns: [table.poolId, table.address] }),
})
);
Indexes
Create indexes using the index()
function in the constraints & indexes argument. The indexing engine creates indexes after historical indexing completes, just before
the app becomes healthy.
import { onchainTable, index } from "@ponder/core";
export const persons = onchainTable(
"persons",
(t) => ({
id: t.text().primaryKey(),
name: t.text(),
}),
(table) => ({
nameIdx: index().on(table.name),
})
);
onchainEnum
The onchainEnum
function accepts two positional arguments. It returns a function that can be used as a column type.
Argument | Type | Description |
---|---|---|
name | string | The SQL enum name. Use snake_case . |
values | string[] | An array of strings representing the allowed values for the enum. |
import { onchainEnum, onchainTable } from "@ponder/core";
export const color = onchainEnum("color", ["ORANGE", "BLACK"]);
export const cats = onchainTable("cats", (t) => ({
name: t.text().primaryKey(),
color: color().notNull(),
}));
Like any other column types, you can use modifiers like .notNull()
, .default()
, and .array()
with enum columns.
// ...
export const dogs = onchainTable("cats", (t) => ({
name: t.text().primaryKey(),
color: color().array().default([]),
}));
relations
Use the relations
function to define relationships between tables.
import { onchainTable, relations } from "@ponder/core";
export const users = onchainTable("users", (t) => ({
id: t.text().primaryKey(),
}));
export const usersRelations = relations(users, ({ one }) => ({
profile: one(profiles, {
fields: [users.id],
references: [profiles.userId],
}),
}));
Relationship types
Type | Method | Description |
---|---|---|
One-to-one | one() | References single related record |
One-to-many | many() | References array of related records |
Many-to-many | Combination | Uses join table with two one-to-many relations |
Read more in the relationships guide and the Drizzle relations documentation.