Pothos v4 is now available! 🎉Check out the full migration guide here

Pothos

Indirect relations

Selecting fields from a nested GraphQL field

By default, the nestedSelection function will return selections based on the type of the current field. nestedSelection can also be used to get a selection from a field nested deeper inside other fields. This is useful if the field returns a type that is not a prismaObject, but a field nested inside the returned type is.

const PostRef = builder.prismaObject('Post', {
  fields: (t) => ({
    title: t.exposeString('title'),
    content: t.exposeString('content'),
    author: t.relation('author'),
  }),
});
 
const PostPreview = builder.objectRef<Post>('PostPreview').implement({
  fields: (t) => ({
    post: t.field({
      type: PostRef,
      resolve: (post) => post,
    }),
    preview: t.string({
      nullable: true,
      resolve: (post) => post.content?.slice(10),
    }),
  }),
});
 
builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    postPreviews: t.field({
      select: (args, ctx, nestedSelection) => ({
        posts: nestedSelection(
          {
            // limit the number of postPreviews to load
            take: 2,
          },
          // Look at the selections in postPreviews.post to determine what relations/fields to select
          ['post'],
          // (optional) If the field returns a union or interface, you can pass a typeName to get selections for a specific object type
          'Post',
        ),
      }),
      type: [PostPreview],
      resolve: (user) => user.posts,
    }),
  }),
});

Indirect relations (eg. Join tables)

If you want to define a GraphQL field that directly exposes data from a nested relationship (many to many relations using a custom join table is a common example of this) you can use the nestedSelection function passed to select.

Given a prisma schema like the following:

model Post {
  id        Int         @id @default(autoincrement())
  title     String
  content   String
  media     PostMedia[]
}

model Media {
  id           Int         @id @default(autoincrement())
  url          String
  posts        PostMedia[]
  uploadedBy   User        @relation(fields: [uploadedById], references: [id])
  uploadedById Int
}

model PostMedia {
  id      Int   @id @default(autoincrement())
  post    Post  @relation(fields: [postId], references: [id])
  media   Media @relation(fields: [mediaId], references: [id])
  postId  Int
  mediaId Int
}

You can define a media field that can pre-load the correct relations based on the graphql query:

const PostDraft = builder.prismaObject('Post', {
  fields: (t) => ({
    title: t.exposeString('title'),
    media: t.field({
      select: (args, ctx, nestedSelection) => ({
        media: {
          select: {
            // This will look at what fields are queried on Media
            // and automatically select uploadedBy if that relation is requested
            media: nestedSelection(
              // This arument is the default query for the media relation
              // It could be something like: `{ select: { id: true } }` instead
              true,
            ),
          },
        },
      }),
      type: [Media],
      resolve: (post) => post.media.map(({ media }) => media),
    }),
  }),
});
 
const Media = builder.prismaObject('Media', {
  select: {
    id: true,
  },
  fields: (t) => ({
    url: t.exposeString('url'),
    uploadedBy: t.relation('uploadedBy'),
  }),
});

On this page