Create & update entities
Ponder's entity store API is inspired by the Prisma Client API (opens in a new tab). The entity store API currently supports the following methods:
create
Insert a new entity into the store.
Options
| name | type | |
|---|---|---|
| id | string | number | bigint | ID of the new entity |
| data | TEntity | Data required for a new entity |
Returns
Promise<TEntity>
Examples
type Token @entity {
id: Int!
mintedBy: String!
mintedAt: Int!
}ponder.on("Blitmap:Mint", async ({ event, context }) => {
const { Token } = context.entities;
const token = await Token.create({
id: event.params.tokenId,
data: {
mintedBy: event.params.to,
mintedAt: event.block.timestamp,
},
});
// { id: 7777, mintedBy: "0x7Df1...", mintedAt: 1679507353 }
});update
Update an entity that already exists.
Options
| name | type | |
|---|---|---|
| id | string | number | bigint | ID of the updated entity |
| data | Partial<TEntity> | Data to update |
| data (function) | (args: { current: TEntity }) => Partial<TEntity> | Function returning data to update |
Returns
Promise<TEntity>
Examples
type Token @entity {
id: Int!
ownedBy: String!
metadataUpdatedAt: Int!
}ponder.on("Blitmap:MetadataUpdate", async ({ event, context }) => {
const { Token } = context.entities;
const token = await Token.update({
id: event.params.tokenId,
data: {
metadataUpdatedAt: event.block.timestamp,
},
});
// { id: 7777, mintedBy: "0x1bA3...", updatedAt: 1679507354 }
});Update function
You can optionally pass a function to the data field that receives the current entity as an argument and returns the update object. This is useful for updates that depend on the current entity, like an incrementing count or balance.
type Account @entity {
id: Int!
balance: BigInt!
}ponder.on("ERC20:Transfer", async ({ event, context }) => {
const { Account } = context.entities;
const recipient = await Account.update({
id: event.params.to,
data: ({ current }) => ({
balance: current.balance + event.params.value,
}),
});
// { id: "0x5D92..", balance: 11800000005n }
});upsert
Update an entity if one already exists with the specified id, or create a new entity.
Options
| name | type | |
|---|---|---|
| id | string | number | bigint | ID of the entity to create or update |
| create | TEntity | Data required for a new entity |
| update | Partial<TEntity> | Data to update |
| update (function) | (args: { current: TEntity }) => Partial<TEntity> | Function returning data to update |
Returns
Promise<TEntity>
Examples
Upsert can be useful for events like the ERC721 Transfer event, which is emitted when a token is minted and whenever a token is transferred.
type Token @entity {
id: Int!
mintedBy: String!
ownedBy: String!
}ponder.on("Blitmap:Transfer", async ({ event, context }) => {
const { Token } = context.entities;
const token = await Token.upsert({
id: event.params.tokenId,
create: {
mintedBy: event.params.to,
ownedBy: event.params.to,
transferCount: 0,
},
update: {
ownedBy: event.params.to,
},
});
// { id: 7777, mintedBy: "0x1bA3...", ownedBy: "0x7F4d..." }
});Update function
You can optionally pass a function to the update field that receives the current entity as an argument and returns the update object. This is useful for updates that depend on the current entity, like an incrementing count or balance.
type Token @entity {
id: Int!
ownedBy: String!
transferCount: Int!
}ponder.on("Blitmap:Transfer", async ({ event, context }) => {
const { Token } = context.entities;
const token = await Token.upsert({
id: event.params.tokenId,
create: {
ownedBy: event.params.to,
transferCount: 0,
},
update: ({ current }) => ({
ownedBy: event.params.to,
transferCount: current.transferCount + 1,
}),
});
// { id: 7777, ownedBy: "0x7F4d...", transferCount: 1 }
});delete
delete deletes an entity by id.
Options
| name | type | |
|---|---|---|
| id | string | number | bigint | ID of the entity to delete |
Returns
Promise<boolean> (true if the entity was deleted, false if it was not found)
Examples
type Player @entity {
id: String!
age: Int!
}await Player.create({ id: "Jim", age: 34 });
const isDeleted = await Player.delete({ id: "Jim" });
// true
const jim = await Player.findUnique({ id: "Jim" });
// nullfindUnique
findUnique finds and returns an entity by id.
Options
| name | type | |
|---|---|---|
| id | string | number | bigint | ID of the entity to find and return |
Returns
Promise<TEntity | null>
Examples
type Player @entity {
id: String!
age: Int!
}await Player.create({ id: "Jim", age: 34 });
const jim = await Player.findUnique({ id: "Jim" });
// { id: "Jim", age: 34 }
const sara = await Player.findUnique({ id: "Sara" });
// nullfindMany
findMany returns a list of entities according to the filter, sort, and pagination options you provide. Note that findMany offers programmatic access to the functionality exposed by the autogenerated GraphQL API.
Options
| name | type | |
|---|---|---|
| where | WhereInput<TEntity> | undefined | Filter matching entities to return |
| orderBy | OrderByInput<TEntity> | undefined | Sort applied to the list |
| skip | number | undefined | Number of records to skip (SQL OFFSET) |
| take | number | undefined | Number of records to take (SQL LIMIT) |
Returns
Promise<TEntity[]>
Examples
Filtering
Filter the result list by passing a where option containing a field name, filter condition, and value. The where option is typed according to the filter conditions available for each field.
type Player @entity {
id: String!
age: Int!
}await Player.create({ id: "Jim", age: 34 });
await Player.create({ id: "Andrew", age: 19 });
await Player.create({ id: "Janet", age: 56 });
const players = await Player.findMany();
// [
// { id: "Jim", age: 34 },
// { id: "Andrew", age: 19 },
// { id: "Janet", age: 56 }
// ]
const players = await Player.findMany({
where: {
id: {
startsWith: "J",
},
},
});
// [
// { id: "Jim", age: 34 },
// { id: "Janet", age: 56 }
// ]If you provide multiple filters, they will be combined with a logical AND.
If you need more complex filters that use logical OR, NOT, or nested
conditions, please open a
discussion (opens in a new tab).
type Player @entity {
id: String!
age: Int!
}await Player.create({ id: "Jim", age: 34 });
await Player.create({ id: "Andrew", age: 19 });
await Player.create({ id: "Janet", age: 56 });
const players = await Player.findMany({
where: {
id: { contains: "e" }
age: { gt: 30 }
}
});
// [
// { id: "Janet", age: 56 }
// ]Sorting
Sort the result list by passing an orderBy option containing a field name and sort direction ("asc" or "desc").
type Player @entity {
id: String!
age: Int!
}await Player.create({ id: "Jim", age: 34 });
await Player.create({ id: "Andrew", age: 19 });
await Player.create({ id: "Janet", age: 56 });
const players = await Player.findMany({
orderBy: {
age: "asc",
},
});
// [
// { id: "Andrew", age: 19 },
// { id: "Jim", age: 34 },
// { id: "Janet", age: 56 }
// ]Pagination
Paginate through the result list using the skip and take options.
Avoid using findMany to return result lists that require pagination. (If you
need this, you're probably doing something wrong. Ask for help.)
type Player @entity {
id: String!
age: Int!
}await Player.create({ id: "Jim", age: 34 });
await Player.create({ id: "Andrew", age: 19 });
await Player.create({ id: "Janet", age: 56 });
await Player.create({ id: "Polly", age: 29 });
const players = await Player.findMany({
orderBy: { age: "desc" },
skip: 1,
take: 2,
});
// [
// { id: "Jim", age: 34 },
// { id: "Polly", age: 29 }
// ]createMany
createMany inserts multiple entities into the store in a single operation. It returns a list of the created entities.
Options
| name | type | |
|---|---|---|
| data | TEntity[] | List of entities to create |
Returns
Promise<TEntity[]>
Examples
type Player @entity {
id: String!
age: Int!
}await Player.createMany({
data: [
{ id: "Jim", age: 34 },
{ id: "Andrew", age: 19 },
{ id: "Janet", age: 56 },
],
});
const players = await Player.findMany();
// [
// { id: "Jim", age: 34 },
// { id: "Andrew", age: 19 },
// { id: "Janet", age: 56 }
// ]updateMany
updateMany updates multiple entities in a single operation using the same update logic. Like the update method, updateMany also optionally accepts an update function.
Options
| name | type | |
|---|---|---|
| where | WhereInput<TEntity> | Filter matching entities to be updated |
| data | Partial<TEntity> | Data to update |
| data (function) | (args: { current: TEntity }) => Partial<TEntity> | Function returning data to update |
Returns
Promise<TEntity[]>
Examples
type Player @entity {
id: String!
age: Int!
}await Player.create({ id: "Jim", age: 34 });
await Player.create({ id: "Andrew", age: 19 });
await Player.create({ id: "Janet", age: 56 });
await Player.updateMany({
where: {
id: {
startsWith: "J",
},
},
data: {
age: 50,
},
});
const players = await Player.findMany();
// [
// { id: "Jim", age: 50 },
// { id: "Andrew", age: 19 },
// { id: "Janet", age: 50 }
// ]