Create & update entities

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

nametype
idstring | number | bigintID of the new entity
dataTEntityData required for a new entity

Returns

Promise<TEntity>

Examples

schema.graphql
type Token @entity {
  id: Int!
  mintedBy: String!
  mintedAt: Int!
}
src/index.ts
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

nametype
idstring | number | bigintID of the updated entity
dataPartial<TEntity>Data to update
data (function)(args: { current: TEntity }) => Partial<TEntity>Function returning data to update

Returns

Promise<TEntity>

Examples

schema.graphql
type Token @entity {
  id: Int!
  ownedBy: String!
  metadataUpdatedAt: Int!
}
src/index.ts
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.

schema.graphql
type Account @entity {
  id: Int!
  balance: BigInt!
}
src/index.ts
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

nametype
idstring | number | bigintID of the entity to create or update
createTEntityData required for a new entity
updatePartial<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.

schema.graphql
type Token @entity {
  id: Int!
  mintedBy: String!
  ownedBy: String!
}
src/index.ts
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.

schema.graphql
type Token @entity {
  id: Int!
  ownedBy: String!
  transferCount: Int!
}
src/index.ts
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

nametype
idstring | number | bigintID of the entity to delete

Returns

Promise<boolean> (true if the entity was deleted, false if it was not found)

Examples

schema.graphql
type Player @entity {
  id: String!
  age: Int!
}
src/index.ts
await Player.create({ id: "Jim", age: 34 });
 
const isDeleted = await Player.delete({ id: "Jim" });
// true
 
const jim = await Player.findUnique({ id: "Jim" });
// null

findUnique

findUnique finds and returns an entity by id.

Options

nametype
idstring | number | bigintID of the entity to find and return

Returns

Promise<TEntity | null>

Examples

schema.graphql
type Player @entity {
  id: String!
  age: Int!
}
src/index.ts
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" });
// null

findMany

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

nametype
whereWhereInput<TEntity> | undefinedFilter matching entities to return
orderByOrderByInput<TEntity> | undefinedSort applied to the list
skipnumber | undefinedNumber of records to skip (SQL OFFSET)
takenumber | undefinedNumber 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.

schema.graphql
type Player @entity {
  id: String!
  age: Int!
}
src/index.ts
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).

schema.graphql
type Player @entity {
  id: String!
  age: Int!
}
src/index.ts
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").

schema.graphql
type Player @entity {
  id: String!
  age: Int!
}
src/index.ts
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.)

schema.graphql
type Player @entity {
  id: String!
  age: Int!
}
src/index.ts
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

nametype
dataTEntity[]List of entities to create

Returns

Promise<TEntity[]>

Examples

schema.graphql
type Player @entity {
  id: String!
  age: Int!
}
src/index.ts
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

nametype
whereWhereInput<TEntity>Filter matching entities to be updated
dataPartial<TEntity>Data to update
data (function)(args: { current: TEntity }) => Partial<TEntity>Function returning data to update

Returns

Promise<TEntity[]>

Examples

schema.graphql
type Player @entity {
  id: String!
  age: Int!
}
src/index.ts
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 }
// ]