PluginsPrisma

Interfaces

Define GraphQL interfaces for a Prisma model and share them across variants.

builder.prismaInterface works exactly like builder.prismaObject, but produces a GraphQL interface instead of an object type. Like prismaObject, it can define either a model's primary type (with name) or a variant (with variant in place of name). Use it to give several variants of one Prisma model a shared set of fields: the interface holds what they have in common, and each variant implements it and adds its own.

This page assumes the generated types and builder are already wired up. The examples add two columns to the base Player model, an isCaptain discriminator and an optional bio:

model Player {
  id        Int     @id @default(autoincrement())
  name      String
  number    Int
  isCaptain Boolean @default(false)
  bio       String?
  // ...team and stats relations as in the base schema
}

An interface with two variants

The interface defines the fields every player shares. A resolveType picks the concrete variant for a given row. Return the type name as a string rather than an object ref, which avoids circular-reference problems between the interface and the variants that implement it.

const Player = builder.prismaInterface('Player', {
  name: 'Player',
  fields: (t) => ({
    id: t.exposeID('id'),
    name: t.exposeString('name'),
  }),
  resolveType: (player) => (player.isCaptain ? 'Captain' : 'SquadPlayer'),
});

builder.prismaObject('Player', {
  variant: 'Captain',
  interfaces: [Player],
  fields: (t) => ({
    isCaptain: t.exposeBoolean('isCaptain'),
  }),
});

builder.prismaObject('Player', {
  variant: 'SquadPlayer',
  interfaces: [Player],
  fields: (t) => ({
    bio: t.exposeString('bio', { nullable: true }),
  }),
});

Both Captain and SquadPlayer are variants of the same Player model, so they inherit its backing shape. Each adds the fields specific to it on top of the interface's id and name.

Selections are not inherited. Under select mode, add the columns you need to both the interface and every implementing object type. Otherwise the object falls back to the default selection of all scalar columns, which may not be what you want.

An interface only spans one model. Trying to have an object for a different Prisma model implement it fails at build time:

// Error at build time: Team is a different model than Player.
builder.prismaObject('Team', {
  interfaces: [Player],
  fields: (t) => ({ id: t.exposeID('id') }),
});