Detalhes do pacote

@pothos/plugin-prisma

hayes141.1kISC4.10.0

A Pothos plugin for more efficient integration with prisma

pothos, graphql, schema, typescript

readme (leia-me)

Prisma Plugin for Pothos

This plugin provides tighter integration with prisma, making it easier to define prisma based object types, and helps solve n+1 queries for relations. It also has integrations for the relay plugin to make defining nodes and connections easy and efficient.

This plugin is NOT required to use prisma with Pothos, but does make things a lot easier and more efficient. See the Using Prisma without a plugin section below for more details.

Features

  • 🎨 Quickly define GraphQL types based on your Prisma models
  • 🦺 Strong type-safety throughout the entire API
  • 🤝 Automatically resolve relationships defined in your database
  • 🎣 Automatic Query optimization to efficiently load the specific data needed to resolve a query (solves common N+1 issues)
  • 💅 Types and fields in GraphQL schema are not implicitly tied to the column names or types in your database.
  • 🔀 Relay integration for defining nodes and connections that can be efficiently loaded.
  • 📚 Supports multiple GraphQL models based on the same Database model
  • 🧮 Count fields can easily be added to objects and connections

Example

Here is a quick example of what an API using this plugin might look like. There is a more thorough breakdown of what the methods and options used in the example below.

// Create an object type based on a prisma model
// without providing any custom type information
builder.prismaObject('User', {
  fields: (t) => ({
    // expose fields from the database
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    bio: t.string({
      // automatically load the bio from the profile
      // when this field is queried
      select: {
        profile: {
          select: {
            bio: true,
          },
        },
      },
      // user will be typed correctly to include the
      // selected fields from above
      resolve: (user) => user.profile.bio,
    }),
    // Load posts as list field.
    posts: t.relation('posts', {
      args: {
        oldestFirst: t.arg.boolean(),
      },
      // Define custom query options that are applied when
      // loading the post relation
      query: (args, context) => ({
        orderBy: {
          createdAt: args.oldestFirst ? 'asc' : 'desc',
        },
      }),
    }),
    // creates relay connection that handles pagination
    // using prisma's built in cursor based pagination
    postsConnection: t.relatedConnection('posts', {
      cursor: 'id',
    }),
  }),
});

// Create a relay node based a prisma model
builder.prismaNode('Post', {
  id: { field: 'id' },
  fields: (t) => ({
    title: t.exposeString('title'),
    author: t.relation('author'),
  }),
});

builder.queryType({
  fields: (t) => ({
    // Define a field that issues an optimized prisma query
    me: t.prismaField({
      type: 'User',
      resolve: async (query, root, args, ctx, info) =>
        prisma.user.findUniqueOrThrow({
          // the `query` argument will add in `include`s or `select`s to
          // resolve as much of the request in a single query as possible
          ...query,
          where: { id: ctx.userId },
        }),
    }),
  }),
});

Given this schema, you would be able to resolve a query like the following with a single prisma query (which will still result in a few optimized SQL queries).

query {
  me {
    email
    posts {
      title
      author {
        id
      }
    }
  }
}

A query like

query {
  me {
    email
    posts {
      title
      author {
        id
      }
    }
    oldPosts: posts(oldestFirst: true) {
      title
      author {
        id
      }
    }
  }
}

Will result in 2 calls to prisma, one to resolve everything except oldPosts, and a second to resolve everything inside oldPosts. Prisma can only resolve each relation once in a single query, so we need a separate to handle the second posts relation.

Install

yarn add @pothos/plugin-prisma

Setup

This plugin requires a little more setup than other plugins because it integrates with the prisma to generate some types that help the plugin better understand your prisma schema. Previous versions of this plugin used to infer all required types from the prisma client itself, but this resulted in a poor dev experience because the complex types slowed down editors, and some more advanced use cases could not be typed correctly.

Add a the pothos generator to your prisma schema

generator pothos {
  provider = "prisma-pothos-types"
}

Now the types Pothos uses will be generated whenever you re-generate your prisma client. Run the following command to re-generate the client and create the new types:

npx prisma generate

additional options:

  • clientOutput: Where the generated code will import the PrismaClient from. The default is the full path of wherever the client is generated. If you are checking in the generated file, using @prisma/client is a good option.
  • output: Where to write the generated types

Example with more options:

generator pothos {
  provider = "prisma-pothos-types"
  clientOutput = "@prisma/client"
  output = "./pothos-types.ts"
}

Set up the builder

import SchemaBuilder from '@pothos/core';
import { PrismaClient } from '@prisma/client';
import PrismaPlugin from '@pothos/plugin-prisma';
// This is the default location for the generator, but this can be
// customized as described above.
// Using a type only import will help avoid issues with undeclared
// exports in esm mode
import type PrismaTypes from '@pothos/plugin-prisma/generated';

const prisma = new PrismaClient({});

const builder = new SchemaBuilder<{
  PrismaTypes: PrismaTypes;
}>({
  plugins: [PrismaPlugin],
  prisma: {
    client: prisma,
    // defaults to false, uses /// comments from prisma schema as descriptions
    // for object types, relations and exposed fields.
    // descriptions can be omitted by setting description to false
    exposeDescriptions: boolean | { models: boolean, fields: boolean },
    // use where clause from prismaRelatedConnection for totalCount (defaults to true)
    filterConnectionTotalCount: true,
    // warn when not using a query parameter correctly
    onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn',
  },
});

It is strongly recommended NOT to put your prisma client into Context. This will result in slower type-checking and a laggy developer experience in VSCode. See this issue for more details.

You can also load or create the prisma client dynamically for each request. This can be used to periodically re-create clients or create read-only clients for certain types of users.

import SchemaBuilder from '@pothos/core';
import { PrismaClient, Prisma } from '@prisma/client';
import PrismaPlugin from '@pothos/plugin-prisma';
import type PrismaTypes from '@pothos/plugin-prisma/generated';

const prisma = new PrismaClient({});

const readOnlyPrisma = new PrismaClient({
  datasources: {
    db: {
      url: process.env.READ_ONLY_REPLICA_URL,
    },
  },
});

const builder = new SchemaBuilder<{
  Context: { user: { isAdmin: boolean } };
  PrismaTypes: PrismaTypes;
}>({
  plugins: [PrismaPlugin],
  prisma: {
    client: (ctx) => (ctx.user.isAdmin ? prisma : readOnlyPrisma),
    // Because the prisma client is loaded dynamically, we need to explicitly provide the some information about the prisma schema
    dmmf: Prisma.dmmf,
  },
});

Creating types with builder.prismaObject

builder.prismaObject takes 2 arguments:

  1. name: The name of the prisma model this new type represents
  2. options: options for the type being created, this is very similar to the options for any other object type
builder.prismaObject('User', {
  // Optional name for the object, defaults to the name of the prisma model
  name: 'PostAuthor',
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
  }),
});

builder.prismaObject('Post', {
  fields: (t) => ({
    id: t.exposeID('id'),
    title: t.exposeString('title'),
  }),
});

So far, this is just creating some simple object types. They work just like any other object type in Pothos. The main advantage of this is that we get the type information without using object refs, or needing imports from prisma client.

Adding prisma fields to non-prisma objects (including Query and Mutation)

There is a new t.prismaField method which can be used to define fields that resolve to your prisma types:

builder.queryType({
  fields: (t) => ({
    me: t.prismaField({
      type: 'User',
      resolve: async (query, root, args, ctx, info) =>
        prisma.user.findUniqueOrThrow({
          ...query,
          where: { id: ctx.userId },
        }),
    }),
  }),
});

This method works just like the normal t.field method with a couple of differences:

  1. The type option must contain the name of the prisma model (eg. User or [User] for a list field).
  2. The resolve function has a new first argument query which should be spread into query prisma query. This will be used to load data for nested relationships.

You do not need to use this method, and the builder.prismaObject method returns an object ref than can be used like any other object ref (with t.field), but using t.prismaField will allow you to take advantage of more efficient queries.

The query object will contain an object with include or select options to pre-load data needed to resolve nested parts of the current query. The included/selected fields are based on which fields are being queried, and the options provided when defining those fields and types.

Adding relations

You can add fields for relations using the t.relation method:

builder.queryType({
  fields: (t) => ({
    me: t.prismaField({
      type: 'User',
      resolve: async (query, root, args, ctx, info) =>
        prisma.user.findUniqueOrThrow({
          ...query,
          where: { id: ctx.userId },
        }),
    }),
  }),
});

builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    posts: t.relation('posts'),
  }),
});

builder.prismaObject('Post', {
  fields: (t) => ({
    id: t.exposeID('id'),
    title: t.exposeString('title'),
    author: t.relation('author'),
  }),
});

t.relation defines a field that can be pre-loaded by a parent resolver. This will create something like { include: { author: true }} that will be passed as part of the query argument of a prismaField resolver. If the parent is another relation field, the includes will become nested, and the full relation chain will be passed to the prismaField that started the chain.

For example the query:

query {
  me {
    posts {
      author {
        id
      }
    }
  }
}

the me prismaField would receive something like the following as its query parameter:

{
  include: {
    posts: {
      include: {
        author: true;
      }
    }
  }
}

This will work perfectly for the majority of queries. There are a number of edge cases that make it impossible to resolve everything in a single query. When this happens Pothos will automatically construct an additional query to ensure that everything is still loaded correctly, and split into as few efficient queries as possible. This process is described in more detail below

Fallback queries

There are some cases where data can not be pre-loaded by a prisma field. In these cases, pothos will issue a findUnique query for the parent of any fields that were not pre-loaded, and select the missing relations so those fields can be resolved with the correct data. These queries should be very efficient, are batched by pothos to combine requirements for multiple fields into one query, and batched by Prisma to combine multiple queries (in an n+1 situation) to a single sql query.

The following are some edge cases that could cause an additional query to be necessary:

  • The parent object was not loaded through a field defined with t.prismaField, or t.relation
  • The root prismaField did not correctly spread the query arguments in is prisma call.
  • The query selects multiple fields that use the same relation with different filters, sorting, or limits
  • The query contains multiple aliases for the same relation field with different arguments in a way that results in different query options for the relation.
  • A relation field has a query that is incompatible with the default includes of the parent object

All of the above should be relatively uncommon in normal usage, but the plugin ensures that these types of edge cases are automatically handled when they do occur.

Filters, Sorting, and arguments

So far we have been describing very simple queries without any arguments, filtering, or sorting. For t.prismaField definitions, you can add arguments to your field like normal, and pass them into your prisma query as needed. For t.relation the flow is slightly different because we are not making a prisma query directly. We do this by adding a query option to our field options. Query can either be a query object, or a method that returns a query object based on the field arguments.

builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    posts: t.relation('posts', {
      // We can define arguments like any other field
      args: {
        oldestFirst: t.arg.boolean(),
      },
      // Then we can generate our query conditions based on the arguments
      query: (args, context) => ({
        orderBy: {
          createdAt: args.oldestFirst ? 'asc' : 'desc',
        },
      }),
    }),
  }),
});

The returned query object will be added to the include section of the query argument that gets passed into the first argument of the parent t.prismaField, and can include things like where, skip, take, and orderBy. The query function will be passed the arguments for the field, and the context for the current request. Because it is used for pre-loading data, and solving n+1 issues, it can not be passed the parent object because it may not be loaded yet.

builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    posts: t.relation('posts', {
      // We can define arguments like any other field
      args: {
        oldestFirst: t.arg.boolean(),
      },
      // Then we can generate our query conditions based on the arguments
      query: (args, context) => ({
        orderBy: {
          createdAt: args.oldestFirst ? 'asc' : 'desc',
        },
      }),
    }),
  }),
});

relationCount

Prisma supports querying for relation counts which allow including counts for relations along side other includes. Before prisma 4.2.0, this does not support any filters on the counts, but can give a total count for a relation. Starting from prisma 4.2.0, filters on relation count are available under the filteredRelationCount preview feature flag.

builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    postCount: t.relationCount('posts', {
      where: {
        published: true,
      },
    }),
  }),
});

Includes on types

In some cases, you may want to always pre-load certain relations. This can be helpful for defining fields directly on type where the underlying data may come from a related table.

builder.prismaObject('User', {
  // This will always include the profile when a user object is loaded.  Deeply nested relations can
  // also be included this way.
  include: {
    profile: true,
  },
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    bio: t.string({
      // The profile relation will always be loaded, and user will now be typed to include the
      // profile field so you can return the bio from the nested profile relation.
      resolve: (user) => user.profile.bio,
    }),
  }),
});

Select mode for types

By default, the prisma plugin will use include when including relations, or generating fallback queries. This means we are always loading all columns of a table when loading it in a t.prismaField or a t.relation. This is usually what we want, but in some cases, you may want to select specific columns instead. This can be useful if you have tables with either a very large number of columns, or specific columns with large payloads you want to avoid loading.

To do this, you can add a select instead of an include to your prismaObject:

builder.prismaObject('User', {
  select: {
    id: true,
  },
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
  }),
});

The t.expose* and t.relation methods will all automatically add selections for the exposed fields WHEN THEY ARE QUERIED, ensuring that only the requested columns will be loaded from the database.

In addition to the t.expose and t.relation, you can also add custom selections to other fields:

builder.prismaObject('User', {
  select: {
    id: true,
  },
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    bio: t.string({
      // This will select user.profile.bio when the the `bio` field is queried
      select: {
        profile: {
          select: {
            bio: true,
          },
        },
      },
      resolve: (user) => user.profile.bio,
    }),
  }),
});

Using arguments or context in your selections

The following is a slightly contrived example, but shows how arguments can be used when creating a selection for a field:

const PostDraft = builder.prismaObject('Post', {
  fields: (t) => ({
    title: t.exposeString('title'),
    commentFromDate: t.string({
      args: {
        date: t.arg({ type: 'Date', required: true }),
      },
      select: (args) => ({
        comments: {
          take: 1,
          where: {
            createdAt: {
              gt: args.date,
            },
          },
        },
      }),
      resolve: (post) => post.comments[0]?.content,
    }),
  }),
});

Extending prisma objects

The normal builder.objectField(s) methods can be used to extend prisma objects, but do not support using selections, or exposing fields not in the default selection. To use these features, you can use

builder.prismaObjectField or builder.prismaObjectFields instead.

Type variants

The prisma plugin supports defining multiple GraphQL types based on the same prisma model. Additional types are called variants. You will always need to have a "Primary" variant (defined as described above). Additional variants can be defined by providing a variant option instead of a name option when creating the type:

const Viewer = builder.prismaObject('User', {
  variant: 'Viewer',
  fields: (t) => ({
    id: t.exposeID('id'),
  });
});

You can define variant fields that reference one variant from another:

const Viewer = builder.prismaObject('User', {
  variant: 'Viewer',
  fields: (t) => ({
    id: t.exposeID('id'),
    // Using the model name ('User') will reference the primary variant
    user: t.variant('User'),
  });
});

const User = builder.prismaNode('User', {
  id: {
    resolve: (user) => user.id,
  },
  fields: (t) => ({
    // To reference another variant, use the returned object Ref instead of the model name:
    viewer: t.variant(Viewer, {
      // return null for viewer if the parent User is not the current user
      isNull: (user, args, ctx) => user.id !== ctx.user.id,
    }),
    email: t.exposeString('email'),
  }),
});

You can also use variants when defining relations by providing a type option:

const PostDraft = builder.prismaNode('Post', {
  variant: 'PostDraft'
  // This set's what database field to use for the nodes id field
  id: { field: 'id' },
  // fields work just like they do for builder.prismaObject
  fields: (t) => ({
    title: t.exposeString('title'),
    author: t.relation('author'),
  }),
});

const Viewer = builder.prismaObject('User', {
  variant: 'Viewer',
  fields: (t) => ({
    id: t.exposeID('id'),
    drafts: t.relation('posts', {
      // This will cause this relation to use the PostDraft variant rather than the default Post variant
      type: PostDraft,
      query: { where: { draft: true } },
    }),
  });
});

You may run into circular reference issues if you use 2 prisma object refs to reference each other. To avoid this, you can split out the field definition for one of the relationships using builder.prismaObjectField

const Viewer = builder.prismaObject('User', {
  variant: 'Viewer',
  fields: (t) => ({
    id: t.exposeID('id'),
    user: t.variant(User),
  });
});

const User = builder.prismaNode('User', {
  interfaces: [Named],
  id: {
    resolve: (user) => user.id,
  },
  fields: (t) => ({
    email: t.exposeString('email'),
  }),
});

// Viewer references the `User` ref in its field definition,
// referencing the `User` in fields would cause a circular type issue
builder.prismaObjectField(Viewer, 'user', t.variant(User));

This same workaround applies when defining relations using variants.

Creating interfaces with builder.prismaInterface

builder.prismaInterface works just like builder.prismaObject and can be used to define either the primary type or a variant for a model.

The following example creates a User interface, and 2 variants Admin and Member. The resolveType method returns the typenames as strings to avoid issues with circular references.

builder.prismaInterface('User', {
  name: 'User',
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
  }),
  resolveType: (user) => {
    return user.isAdmin ? 'Admin' : 'Member';
  },
});

builder.prismaObject('User', {
  variant: 'Admin',
  interfaces: [User],
  fields: (t) => ({
    isAdmin: t.exposeBoolean('isAdmin'),
  }),
});

builder.prismaObject('User', {
  variant: 'Member',
  interfaces: [User],
  fields: (t) => ({
    bio: t.exposeString('bio'),
  }),
});

When using select mode, it's recommended to add selections to both the interface and the object types that implement them. Selections are not inherited and will fallback to the default selection which includes all scalar columns.

You will not be able to extend an interface for a different prisma model, doing so will result in an error at build time.

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'),
  }),
});

Detecting unused query arguments

Forgetting to spread the query argument from t.prismaField or t.prismaConnection into your prisma query can result in inefficient queries, or even missing data. To help catch these issues, the plugin can warn you when you are not using the query argument correctly.

the onUnusedQuery option can be set to warn or error to enable this feature. When set to warn it will log a warning to the console if Pothos detects that you have not properly used the query in your resolver. Similarly if you set the option to error it will throw an error instead. You can also pass a function which will receive the info object which can be used to log or throw your own error.

This check is fairly naive and works by wrapping the properties on the query with a getter that sets a flag if the property is accessed. If no properties are accessed on the query object before the resolver returns, it will trigger the onUnusedQuery condition.

It's recommended to enable this check in development to more quickly find potential issues.

Optimized queries without t.prismaField

In some cases, it may be useful to get an optimized query for fields where you can't use t.prismaField.

This may be required for combining with other plugins, or because your query does not directly return a PrismaObject. In these cases, you can use the queryFromInfo helper. An example of this might be a mutation that wraps the prisma object in a result type.

const Post = builder.prismaObject('Post', {...});

builder.objectRef<{
  success: boolean;
  post?: Post
  }>('CreatePostResult').implement({
  fields: (t) => ({
    success: t.boolean(),
    post: t.field({
      type: Post,
      nullable:
      resolve: (result) => result.post,
    }),
  }),
});

builder.mutationField(
  'createPost',
  {
    args: (t) => ({
      title: t.string({ required: true }),
      ...
    }),
  },
  {
    resolve: async (parent, args, context, info) => {
      if (!validateCreatePostArgs(args)) {
        return {
          success: false,
        }
      }

      const post = prisma.city.create({
        ...queryFromInfo({
          context,
          info,
          // nested path where the selections for this type can be found
          path: ['post']
          // optionally you can pass a custom initial selection, generally you wouldn't need this
          // but if the field at `path` is not selected, the initial selection set may be empty
          select: {
            comments: true,
          },
        }),
        data: {
          title: args.input.title,
          ...
        },
      });

      return {
        success: true,
        post,
      }
    },
  },
);

Relay integration

This plugin has extensive integration with the relay plugin, which makes creating nodes and connections very easy.

prismaNode

The prismaNode method works just like the prismaObject method with a couple of small differences:

  • there is a new id option that mirrors the id option from node method of the relay plugin, and must contain a resolve function that returns the id from an instance of the node. Rather than defining a resolver for the id field, you can set the field option to the name of a unique column or index.
builder.prismaNode('Post', {
  // This set's what database field to use for the nodes id field
  id: { field: 'id' },
  // fields work just like they do for builder.prismaObject
  fields: (t) => ({
    title: t.exposeString('title'),
    author: t.relation('author'),
  }),
});

If you need to customize how ids are formatted, you can add a resolver for the id, and provide a findUnique option that can be used to load the node by it's id. This is generally not necessary.

builder.prismaNode('Post', {
  id: { resolve: (post) => String(post.id) },
  // The return value will be passed as the `where` of a `prisma.post.findUnique`
  findUnique: (id) => ({ id: Number.parseInt(id, 10) }),
  fields: (t) => ({
    title: t.exposeString('title'),
    author: t.relation('author'),
  }),
});

When executing the node(id: ID!) query with a global ID for which prisma cannot find a record in the database, the default behavior is to throw an error. There are some scenarios where it is preferable to return null instead of throwing an error. For this you can add the nullable: true option:

builder.prismaNode('Post', {
  id: { resolve: (post) => String(post.id) },
  nullable: true,
  fields: (t) => ({
    title: t.exposeString('title'),
    author: t.relation('author'),
  }),
});

prismaConnection

The prismaConnection method on a field builder can be used to create a relay connection field that also pre-loads all the data nested inside that connection.

builder.queryType({
  fields: (t) => ({
    posts: t.prismaConnection(
      {
        type: 'Post',
        cursor: 'id',
        resolve: (query, parent, args, context, info) => prisma.post.findMany({ ...query }),
      },
      {}, // optional options for the Connection type
      {}, // optional options for the Edge type),
    ),
  }),
});

options

  • type: the name of the prisma model being connected to
  • cursor: a @unique column of the model being connected to. This is used as the cursor option passed to prisma.
  • defaultSize: (default: 20) The default page size to use if first and last are not provided.
  • maxSize: (default: 100) The maximum number of nodes returned for a connection.
  • resolve: Like the resolver for prismaField, the first argument is a query object that should be spread into your prisma query. The resolve function should return an array of nodes for the connection. The query will contain the correct take, skip, and cursor options based on the connection arguments (before, after, first, last), along with include options for nested selections.
  • totalCount: A function for loading the total count for the connection. This will add a totalCount field to the connection object. The totalCount method will receive (connection, args, context, info) as arguments. Note that this will not work when using a shared connection object (see details below)

The created connection queries currently support the following combinations of connection arguments:

  • first, last, or before
  • first and before
  • last and after

Queries for other combinations are not as useful, and generally requiring loading all records between 2 cursors, or between a cursor and the end of the set. Generating query options for these cases is more complex and likely very inefficient, so they will currently throw an Error indicating the argument combinations are not supported.

The maxSize and defaultSize can also be configured globally using maxConnectionSize and defaultConnectionSize options in the prisma plugin options.

relatedConnection

The relatedConnection method can be used to create a relay connection field based on a relation of the current model.

builder.prismaNode('User', {
  id: { field: 'id' },
  fields: (t) => ({
    // Connections can be very simple to define
    simplePosts: t.relatedConnection('posts', {
      cursor: 'id',
    }),
    // Or they can include custom arguments, and other options
    posts: t.relatedConnection(
      'posts',
      {
        cursor: 'id',
        args: {
          oldestFirst: t.arg.boolean(),
        },
        query: (args, context) => ({
          orderBy: {
            createdAt: args.oldestFirst ? 'asc' : 'desc',
          },
        }),
      },
      {}, // optional options for the Connection type
      {}, // optional options for the Edge type),
    ),
  }),
});

options

  • cursor: a @unique column of the model being connected to. This is used as the cursor option passed to prisma.
  • defaultSize: (default: 20) The default page size to use if first and last are not provided.
  • maxSize: (default: 100) The maximum number of nodes returned for a connection.
  • query: A method that accepts the args and context for the connection field, and returns filtering and sorting logic that will be merged into the query for the relation.
  • totalCount: when set to true, this will add a totalCount field to the connection object. see relationCount above for more details. Note that this will not work when using a shared connection object (see details below)

Indirect relations as connections

Creating connections from indirect relations is a little more involved, but can be achieved using prismaConnectionHelpers with a normal t.connection field.

// Create a prisma object for the node type of your connection
const Media = builder.prismaObject('Media', {
  select: {
    id: true,
  },
  fields: (t) => ({
    url: t.exposeString('url'),
  }),
});

// Create connection helpers for the media type.  This will allow you
// to use the normal t.connection with a prisma type
const mediaConnectionHelpers = prismaConnectionHelpers(
  builder,
  'PostMedia', // this should be the the join table
  {
    cursor: 'id',
    select: (nodeSelection) => ({
      // select the relation to the media node using the nodeSelection function
      media: nodeSelection({
        // optionally specify fields to select by default for the node
        select: {
          id: true,
          posts: true,
        },
      }),
    }),
    // resolve the node from the edge
    resolveNode: (postMedia) => postMedia.media,
    // additional/optional options
    maxSize: 100,
    defaultSize: 20,
  },
);

builder.prismaObjectField('Post', 'mediaConnection', (t) =>
  t.connection({
    // The type for the Node
    type: Media,
    // since we are not using t.relatedConnection we need to manually
    // include the selections for our connection
    select: (args, ctx, nestedSelection) => ({
      media: mediaConnectionHelpers.getQuery(args, ctx, nestedSelection),
    }),
    resolve: (post, args, ctx) =>
      // This helper takes a list of nodes and formats them for the connection
      mediaConnectionHelpers.resolve(
        // map results to the list of edges
        post.media,
        args,
        ctx,
      ),
  }),
);

The above example assumes that you are paginating a relation to a join table, where the pagination args are applied based on the relation to that join table, but the nodes themselves are nested deeper.

prismaConnectionHelpers can also be used to manually create a connection where the edge and connections share the same model, and pagination happens directly on a relation to nodes type (even if that relation is nested).

const commentConnectionHelpers = prismaConnectionHelpers(builder, 'Comment', {
  cursor: 'id',
});

const SelectPost = builder.prismaObject('Post', {
  fields: (t) => ({
    title: t.exposeString('title'),
    comments: t.connection({
      type: commentConnectionHelpers.ref,
      select: (args, ctx, nestedSelection) => ({
        comments: commentConnectionHelpers.getQuery(args, ctx, nestedSelection),
      }),
      resolve: (parent, args, ctx) => commentConnectionHelpers.resolve(parent.comments, args, ctx),
    }),
  }),
});

To add arguments for a connection defined with a helper, it is often easiest to define the arguments on the connection field rather than the connection helper. This allows connection helpers to be shared between fields that may not share the same arguments:

const mediaConnectionHelpers = prismaConnectionHelpers(builder, 'PostMedia', {
  cursor: 'id',
  select: (nodeSelection) => ({
    media: nodeSelection({}),
  }),
  resolveNode: (postMedia) => postMedia.media,
});

builder.prismaObjectField('Post', 'mediaConnection', (t) =>
  t.connection({
    type: Media,
    args: {
      inverted: t.arg.boolean(),
    },
    select: (args, ctx, nestedSelection) => ({
      media: {
        ...mediaConnectionHelpers.getQuery(args, ctx, nestedSelection),
        orderBy: {
          post: {
            createdAt: args.inverted ? 'desc' : 'asc',
          },
        },
      },
    }),
    resolve: (post, args, ctx) => mediaConnectionHelpers.resolve(post.media, args, ctx),
  }),
);

Arguments, ordering and filtering can also be defined on the helpers themselves:

const mediaConnectionHelpers = prismaConnectionHelpers(builder, 'PostMedia', {
  cursor: 'id',
  // define arguments for the connection helper, these will be available as the second argument of `select`
  args: (t) => ({
    inverted: t.arg.boolean(),
  }),
  select: (nodeSelection, args) => ({
    media: nodeSelection({}),
  }),
  query: (args) => ({
    // Custom filtering with a where clause
    where: {
      post: {
        published: true,
      },
    },
    // custom ordering including use of args
    orderBy: {
      post: {
        createdAt: args.inverted ? 'desc' : 'asc',
      },
    },
  }),
  resolveNode: (postMedia) => postMedia.media,
});

builder.prismaObjectField('Post', 'mediaConnection', (t) =>
  t.connection({
    type: Media,
    // add the args from the connection helper to the field
    args: mediaConnectionHelpers.getArgs(),
    select: (args, ctx, nestedSelection) => ({
      media: mediaConnectionHelpers.getQuery(args, ctx, nestedSelection),
    }),
    resolve: (post, args, ctx) => mediaConnectionHelpers.resolve(post.media, args, ctx),
  }),
);

Sharing Connections objects

You can create reusable connection objects by using builder.connectionObject.

These connection objects can be used with t.prismaConnection, t.relatedConnection, or t.connection

Shared edges can also be created using t.edgeObject

const CommentConnection = builder.connectionObject({
  type: Comment,
  // or
  type: commentConnectionHelpers.ref,
  name: 'CommentConnection',
});

builder.prismaObject('Post', {
  fields: (t) => ({
    id: t.exposeID('id'),
    ...
    commentsConnection: t.relatedConnection(
      'comments',
      { cursor: 'id' },
      // The connection object ref can be passed in place of the connection object options
      CommentConnection
    ),
  }),
});

Extending connection edges

In some cases you may want to expose some data from an indirect connection on the edge object.

const mediaConnectionHelpers = prismaConnectionHelpers(builder, 'PostMedia', {
  cursor: 'id',
  select: (nodeSelection) => ({
    // select the relation to the media node using the nodeSelection function
    media: nodeSelection({}),
    // Select additional fields from the join table
    createdAt: true,
  }),
  // resolve the node from the edge
  resolveNode: (postMedia) => postMedia.media,
});

builder.prismaObjectFields('Post', (t) => ({
  manualMediaConnection: t.connection(
    {
      type: Media,
      select: (args, ctx, nestedSelection) => ({
        media: mediaConnectionHelpers.getQuery(args, ctx, nestedSelection),
        select: {
          media: nestedSelection({}, ['edges', 'node']),
        },
      }),

      resolve: (post, args, ctx) =>
        mediaConnectionHelpers.resolve(
          post.media.map(({ media }) => media),
          args,
          ctx,
        ),
    },
    {},
    // options for the edge object
    {
      // define the additional fields on the edge object
      fields: (edge) => ({
        createdAt: edge.field({
          type: 'DateTime',
          // the parent shape for edge fields is inferred from the connections resolve function
          resolve: (media) => media.createdAt,
        }),
      }),
    },
  ),
}));

Total count on shared connection objects

If you are set the totalCount: true on a prismaConnection or relatedConnection field, and are using a custom connection object, you will need to manually add the totalCount field to the connection object manually. The parent object on the connection will have a totalCount property that is either a the totalCount, or a function that will return the totalCount.

const CommentConnection = builder.connectionObject({
  type: Comment,
  name: 'CommentConnection',
  fields: (t) => ({
    totalCount: t.int({
      resolve: (connection) => {
        const { totalCount } = connection as {
          totalCount?: number | (() => number | Promise<number>);
        };

        return typeof totalCount === 'function' ? totalCount() : totalCount;
      },
    }),
  }),
});

If you want to add a global totalCount field, you can do something similar using builder.globalConnectionField:

export const builder = new SchemaBuilder<{
  PrismaTypes: PrismaTypes;
  Connection: {
    totalCount: number | (() => number | Promise<number>);
  };
}>({
  plugins: [PrismaPlugin, RelayPlugin],
  relayOptions: {},
  prisma: {
    client: db,
  },
});

builder.globalConnectionField('totalCount', (t) =>
  t.int({
    nullable: false,
    resolve: (parent) =>
      typeof parent.totalCount === 'function' ? parent.totalCount() : parent.totalCount,
  }),
);

Relay Utils

parsePrismaCursor and formatPrismaCursor

These functions can be used to manually parse and format cursors that are compatible with prisma connections.

Parsing a cursor will return the value from the column used for the cursor (often the id), this value may be an array or object when a compound index is used as the cursor. Similarly, to format a cursor, you must provide the column(s) that make up the cursor.

Using Prisma without a plugin

Using prisma without a plugin is relatively straight forward using the builder.objectRef method.

The easiest way to create types backed by prisma looks something like:

import { Post, PrismaClient, User } from '@prisma/client';

const db = new PrismaClient();
const UserObject = builder.objectRef<User>('User');
const PostObject = builder.objectRef<Post>('Post');

UserObject.implement({
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    posts: t.field({
      type: [PostObject],
      resolve: (user) =>
        db.post.findMany({
          where: { authorId: user.id },
        }),
    }),
  }),
});

PostObject.implement({
  fields: (t) => ({
    id: t.exposeID('id'),
    title: t.exposeString('title'),
    author: t.field({
      type: UserObject,
      resolve: (post) => db.user.findUniqueOrThrow({ where: { id: post.authorId } }),
    }),
  }),
});

builder.queryType({
  fields: (t) => ({
    me: t.field({
      type: UserObject,
      resolve: (root, args, ctx) => db.user.findUniqueOrThrow({ where: { id: ctx.userId } }),
    }),
  }),
});

This sets up User, and Post objects with a few fields, and a me query that returns the current user. There are a few things to note in this setup:

  1. We split up the builder.objectRef and the implement calls, rather than calling builder.objectRef(...).implement(...). This prevents typescript from getting tripped up by the circular references between posts and users.
  2. We use findUniqueOrThrow because those fields are not nullable. Using findUnique, prisma will return a null if the object is not found. An alternative is to mark these fields as nullable.
  3. The refs to our object types are called UserObject and PostObject, this is because User and Post are the names of the types imported from prisma. We could instead alias the types when we import them so we can name the refs to our GraphQL types after the models.

This setup is fairly simple, but it is easy to see the n+1 issues we might run into. Prisma helps with this by batching queries together, but there are also things we can do in our implementation to improve things.

One thing we could do if we know we will usually be loading the author any time we load a post is to make the author part of shape required for a post:

const UserObject = builder.objectRef<User>('User');
// We add the author here in the objectRef
const PostObject = builder.objectRef<Post & { author: User }>('Post');

UserObject.implement({
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    posts: t.field({
      type: [PostObject],
      resolve: (user) =>
        db.post.findMany({
          // We now need to include the author when we query for posts
          include: {
            author: true,
          },
          where: { authorId: user.id },
        }),
    }),
  }),
});

PostObject.implement({
  fields: (t) => ({
    id: t.exposeID('id'),
    title: t.exposeString('title'),
    author: t.field({
      type: UserObject,
      // Now we can just return the author from the post instead of querying for it
      resolve: (post) => post.author,
    }),
  }),
});

We may not always want to query for the author though, so we could make the author optional and fall back to using a query if it was not provided by the parent resolver:

const PostObject = builder.objectRef<Post & { author?: User }>('Post');

PostObject.implement({
  fields: (t) => ({
    id: t.exposeID('id'),
    title: t.exposeString('title'),
    author: t.field({
      type: UserObject,
      resolve: (post) =>
        post.author ?? db.user.findUnique({ rejectOnNotFound: true, where: { id: post.authorId } }),
    }),
  }),
});

With this setup, a parent resolver has the option to include the author, but we have a fallback incase it does not.

There are other patterns like data loaders than can be used to reduce n+1 issues, and make your graph more efficient, but they are too complex to describe here.

Input types

To create input types compatible with the prisma client, you can check out the prisma-utils plugin

changelog (log de mudanças)

Change Log

4.10.0

Minor Changes

  • 12713e5: improve handling of iterable connections

4.9.1

Patch Changes

  • 1622740: update dependencies
  • 1622740: revert change that omits the path from prisma generator and updated import path the import from /client.js for new generator

4.9.0

Minor Changes

  • 9d6a30c: Import from prisma directory instead of index.ts

4.8.2

Patch Changes

  • 3d5c989: Fix esm prisma output location

4.8.1

Patch Changes

  • cd7f309: Update dependencies

4.8.0

Minor Changes

  • 7d03f42: Allow __typename for queries that only need to select count on connections

4.7.0

Minor Changes

  • e149c85: Import types from prismaOutput/index.js instead of directly from prismaOutput to improve esm compatability

4.6.0

Minor Changes

  • 955229a: Add support for new prisma-client generator

4.5.0

Minor Changes

  • 2c0e072: Skip querying field selections within @deferred fragments

4.4.1

Patch Changes

  • d874bce: Improve inference of multiple interfaces

4.4.0

Minor Changes

  • ea9981f: Support prisma 6.0

4.3.1

Patch Changes

  • 52a70e9: Load client when db request is initialized rather than caching on model loader

4.3.0

Minor Changes

  • aadc82c: export client cache so it can be reset during request

4.2.2

Patch Changes

  • 2f91f2b: Fix ref relay options in plugin-prisma

4.2.1

Patch Changes

  • fc44ea7: Fix a few cases where passing PrismaRefs for field types breaks model inference

4.2.0

Minor Changes

  • 27af377: replace eslint and prettier with biome

4.1.0

Minor Changes

  • 307340a: Improve queryFromInfo typing
  • 307340a: Use a shared directive name for defining indirect resolutions

4.0.5

Patch Changes

  • 094396d: Fix t.withAuth used with prismaField and PrismaObjectRefs

4.0.4

Patch Changes

  • a95bd0a: Add support for composite types when inferring PrismaTypes from client (thanks to @2coo)

4.0.3

Patch Changes

  • Updated dependencies [777f6de]
    • @pothos/core@4.0.2

4.0.2

Patch Changes

  • 9bd203e: Fix graphql peer dependency version to match documented minumum version
  • Updated dependencies [9bd203e]
    • @pothos/core@4.0.1

4.0.1

Patch Changes

  • 132a2a5: Fix empty generated.ts file

4.0.0

Major Changes

Patch Changes

  • 79139e8: rebuild generator
  • c1e6dcb: update readmes
  • b2de1f2: fix datamodel generator
  • bdcb8cd: Fix prismaFieldWithInput
  • Updated dependencies [c1e6dcb]
  • Updated dependencies [29841a8]
    • @pothos/core@4.0.0

4.0.0-next.4

Patch Changes

  • update readmes
  • Updated dependencies
    • @pothos/core@4.0.0-next.1

4.0.0-next.3

Patch Changes

  • 924ae0b: rebuild generator

4.0.0-next.2

Patch Changes

  • fix datamodel generator

4.0.0-next.1

Patch Changes

  • Fix prismaFieldWithInput

4.0.0-next.0

Major Changes

Patch Changes

  • Updated dependencies [29841a8]
    • @pothos/core@4.0.0-next.0

3.65.3

Patch Changes

  • 1ecea46: revert accidental pinning of graphql peer dependency

3.65.2

Patch Changes

  • ae7d3fb: Fix prisma esm import on windows

3.65.1

Patch Changes

  • f763170: Add delete method to context caches

3.65.0

Minor Changes

  • 3698a15: Accept a typeName for nestedSelection helper to allow optimized sub-selections on abstract types

3.64.0

Minor Changes

  • 22d4118: Add maxConnectionSize and defaultConnectionSize options
  • 22d4118: Fix type issue where using select on a type or field for a nullable relation would result in the relation being non-nullable on the parent object

3.63.1

Patch Changes

  • 9606ed4: [plugin-prisma] Fix prisma generator not creating the ESM build on windows

3.63.0

Minor Changes

  • 7672c13: Add support for adding args, and query with where/orderBy to prismaConnectionHelpers

3.62.0

Minor Changes

  • b3cb5fd: Arguments support for prisma relation count

3.61.0

Minor Changes

  • 0be6da5c: Fix issue with detection for totalCount only selections

3.60.0

Minor Changes

  • 018c797c: Prevent loading full relation on a prisma connection when only total count is selected

3.59.3

Patch Changes

  • 094359cd: Expose prismaModelKey

3.59.2

Patch Changes

  • 0d8d60fa: add another case for @skip and @include when generating prisma selections

3.59.1

Patch Changes

  • 1fc5b60b: Support Client Directives in Prisma plugin (@skip and @include)

3.59.0

Minor Changes

  • 1bbd3d70: update model loader to cache query mappings and batch compatible queries to reduce likelyhood of prisma deoptimization

3.58.0

Minor Changes

  • 4ebfa27b: Add prismaInterfaceField(s) method

Patch Changes

  • 4ebfa27b: Fix bug that ignored differences in Date values when checking compatability between selections

3.57.0

Minor Changes

  • c7756128: Improve typing for t.expose methods when strict mode is disabled

3.56.1

Patch Changes

  • 016011f2: Fix custom descriptions in t.expose methods

3.56.0

Minor Changes

  • Fix unused query check for prismaConnections

3.55.0

Minor Changes

  • 39237239: Add builder.prismaInterface to allow interface variants of a prisma model

3.54.0

Minor Changes

  • 7494da05: Add onUnusedQuery option to the prisma plugin options

3.53.0

Minor Changes

  • 5d3f7b97: Improve inference for t.expose methods on prisma field builder

3.52.0

Minor Changes

  • 624f2d05: Add optimizations for nodes field on connections

3.51.1

Patch Changes

  • e8139e73: Fixed a bug where totalCount was not selected correctly when selected through a fragment

3.51.0

Minor Changes

  • dbdb6f03: Fix compatability with prisma@4.13.*

3.50.1

Patch Changes

  • 5b6007cd: Prevent unavailable prisma CreateInput types from being referenced by pothos generated types.

3.50.0

Minor Changes

  • 27b0638d: Update plugin imports so that no named imports are imported from files with side-effects

3.49.1

Patch Changes

  • b39e7eab: Fix bug where queryFromInfo would default to the wrong type when setting path without also specifying a typeName

3.49.0

Minor Changes

  • 0c042150: Allow globalConnectionFields to be overwritten on specific connections

3.48.0

Minor Changes

  • b3259d3e: Make parent and args available in connection and edge fields of prisma connections

3.47.3

Patch Changes

  • 4c6bc638: Add provinance to npm releases

3.47.2

Patch Changes

  • 14f8cd5c: Fix returning nulls from nullable prismaConnections

3.47.1

Patch Changes

  • 80c62446: Fix issue with connection helpers and extendedWhereUnique

3.47.0

Minor Changes

  • 5ea5ce24: Allow t.relatedConnection to override take, skip, and cursor in the query option

3.46.0

Minor Changes

  • 1878d5d9: Allow readonly arrays in more places

3.45.0

Minor Changes

  • e5295551: Add initial support for mutation input in prisma utils
  • 72bd678a: Add new prismaUtils feature flag to the generator

3.44.0

Minor Changes

  • 07bf6d4f: Simplify how relations are defined in PrismaTypes
  • 93bd2842: Support typescript@5.0

3.43.0

Minor Changes

  • e8d75349: - allow connection fields (edges / pageInfo) to be promises
    • add completeValue helper to core for unwrapping MaybePromise values
    • set nodes as null if edges is null and the field permits a null return

3.42.0

Minor Changes

  • 384bc124: Add nullability option to prismaNode

3.41.3

Patch Changes

  • 853f3cfb: Fix hasPreviousPage for connections using only last

3.41.2

Patch Changes

  • 687c6e2d: Fix last when used without before

3.41.1

Patch Changes

  • 592ffd3b: Fix name option for prismaNode

3.41.0

Minor Changes

  • bf0385ae: Add new PothosError classes

3.40.3

Patch Changes

  • 372260ec: Fix bug that prevented prisma from correctly including selections when using the directResult option from the errors plugin

3.40.2

Patch Changes

  • 98c6e801: Fix issue when using path and typeName together in resolveQueryFromInfo

3.40.1

Patch Changes

  • 5c6e0abb: Add placeholder generated file with instructions to run prisma generate

3.40.0

Minor Changes

  • 75d13217: Export utils for formatting prisma cursors

3.39.0

Minor Changes

  • c3db3bcd: Enable adding interfaces to connections and edges

3.38.1

Patch Changes

  • 943cb073: import from @prisma/client/index.js for esm generated types

3.38.0

Minor Changes

  • 41426ee7: Add export specifier and esm output for generated prisma types

3.37.0

Minor Changes

  • 26774fa0: Rewrite prismaConnectionHelpers to properly work with indirect relations

3.36.0

Minor Changes

  • 8841e861: Add builder.prismaObjectField(s) method to extend prisma objects and simplify defining circular relationships
  • cd1c0502: Add support for nested lists
  • 99bc6574: Add initial support for reusable prisma connections

3.35.8

Patch Changes

  • d4d41796: Update dev dependencies

3.35.7

Patch Changes

  • b6be576d: Fix typing for nullable prisma connections

3.35.6

Patch Changes

  • 6f00194c: Fix an issue with esm import transform

3.35.5

Patch Changes

  • b12f9122: Fix issue with esm build script

3.35.4

Patch Changes

  • 9fa27cf7: Transform dynamic type imports in d.ts files

3.35.3

Patch Changes

  • 3a82d645: Apply esm transform to esm d.ts definitions

3.35.2

Patch Changes

  • 218fc68b: Fix script for copying ems d.ts definitions

3.35.1

Patch Changes

  • 67531f1e: Create separate typescript definitions for esm files

3.35.0

Minor Changes

  • 11929311: Update type definitions to work with module: "nodeNext"

3.34.1

Patch Changes

  • aa18acb7: update dev dependencies

3.34.0

Minor Changes

  • a76616e0: Add prismaFieldWithInput method

3.33.0

Minor Changes

  • cf93c7c9: Fix some edge cases with how option objects become optional when no arguments are required

3.32.0

Minor Changes

  • d67764b5: Make options objecst on toSchema, queryType, and mutationType optional

3.31.1

Patch Changes

  • 47fea5ed: Fix: add .ts extension to filename for generated prisma fields

3.31.0

Minor Changes

  • 50a60d92: Support prisma filtered relations counts

Patch Changes

  • e297e78a: Support typescript@4.8

3.30.0

Minor Changes

  • 521cde32: Improve how default output location for prisma types is calculated

3.29.0

Minor Changes

  • 76d50bb4: Fix import of cjs graphql file in esm pothos

3.28.0

Minor Changes

  • 390e74a7: Add idFieldOptions to relay plugin options

3.27.2

Patch Changes

  • 193ac71a: Support fullTextSearch types (#553)

3.27.1

Patch Changes

  • 222298f0: update curor type on query arg of prismaConnections

3.27.0

Minor Changes

  • c5b1e2d3: move addBrand and hasBrand from PrismaNodeRef to PrismaObjectRef so it can be used with all prisma objects
  • c5b1e2d3: Only use abstractReturnShapeKey when resolveType is not provided

3.26.0

Minor Changes

  • 5423703a: expose queryFromInfo from prisma plugin

3.25.0

Minor Changes

  • 82596ec2: remove duplicate Fields from generated prisma types

3.24.0

Minor Changes

  • 5e71c283: update queryFromInfo to support indirect paths

3.23.0

Minor Changes

  • 33789284: Update cursor encoding to work in deno
  • 33789284: Fix default connection size when using "before"
  • 33789284: Support setting max and default cursor sizes based in args or context

3.22.0

Minor Changes

  • 13216a3d: remove all remaining circular imports

3.21.2

Patch Changes

  • c102f522: Fix withAuth on prismaObject fields builders

3.21.1

Patch Changes

  • a02b25c2: Fix regression in compatibility between prisma and simple objects plugins"

3.21.0

Minor Changes

  • 3ead60ae: Add option to use comments from prisma schema as graphql descriptions

Patch Changes

  • 3ead60ae: update dev deps

3.20.0

Minor Changes

  • f7f74585: Add option for configuring name of id field for relay nodes

3.19.0

Minor Changes

  • 6382f65b: Fix types when using prismaField with select on a PrismaObject

3.18.0

Minor Changes

  • 360836e5: Fix issue with prismaField not inferring parent thpe for subscriptions

3.17.0

Minor Changes

  • c50b9197: Support BigInt cursors

3.16.0

Minor Changes

  • 86c16787: Allow dmmf to be passed from Prisma.dmmf

3.15.0

Minor Changes

  • dad7fb43: Fix typing for fallback resolvers on relation fields, and correclty pass all query properties for relatedConnections

3.14.0

Minor Changes

  • 3a7ff291: Refactor internal imports to remove import cycles

Patch Changes

  • 3a7ff291: Update dev dependencies

3.13.2

Patch Changes

  • f58ad8fa: Fix type error introduced by withAuth helper

3.13.1

Patch Changes

  • 04ed2b0c: Fix args in plugin methods on connection fields sometimes not being typed correctly

3.13.0

Minor Changes

  • 7311904e: Support uniqueIndexes as connection cursors
  • 7311904e: Add withAuth method to return a field builder to allow custom auth context with other plugin methods
  • 7311904e: Use findUniqueOrThrow rather than rejectOnNotFound if available

Patch Changes

  • 7311904e: Fix connection with empty select
  • 7311904e: Update dev deps

3.12.1

Patch Changes

  • c8f75aa1: Update dev dependencies

3.12.0

Minor Changes

  • 4d414fb5: Add support for prisma@4

3.11.0

Minor Changes

  • 79e69c2b: Add resolveCursorConnection helper for relay plugin

3.10.0

Minor Changes

  • 384b0fb6: Make findUnique optional by defaulting to id/unique fields defined in prisma schema

3.9.0

Minor Changes

  • e090a835: Add fieldWithSelection method to support indirect relions

Patch Changes

  • 4e5756ca: Update dev dependencies

3.8.0

Minor Changes

  • 4154edc9: Add isNull option to prisma variant fields

3.7.0

Minor Changes

  • ecb2714c: Add types entry to export map in package.json and update dev dependencies

    This should fix compatibility with typescripts new "moduleResolution": "node12"

3.6.1

Patch Changes

  • 205a8c73: Recactor internal imports to reduce imports from index files

3.6.0

Minor Changes

  • ce1063e3: Add new tracinig packages

Patch Changes

  • ce1063e3: Fix issue with fields selects when created created with functions

3.5.0

Minor Changes

  • 05163ca5: Add support for dynamically loading prisma client and selecting counts in field level selects"

3.4.0

Minor Changes

  • a8e31a70: Improve user experience when srtict mode is disabled

3.3.0

Minor Changes

  • 241a385f: Add peer dependency on @pothos/core

3.2.0

Minor Changes

  • 6279235f: Update build process to use swc and move type definitions to dts directory

Patch Changes

  • 21a2454e: update dev dependencies

3.1.2

Patch Changes

  • 1bf0cd00: Add typescript version check for prisma generator

3.1.1

Patch Changes

  • 86718e08: Make lookups on extensions objects compatible with older graphql versions

3.1.0

Minor Changes

  • 8add0378: Add totalCount option to prismaConnection

3.0.0

Minor Changes

  • 9b6353d4: Use Promise.resolve instead of setTimeout to batch fallback operations

0.19.0

Minor Changes

  • cf4a2d14: Add support for using selects instead of includes in queries

0.18.0

Minor Changes

  • ad8d119b: Add support for composite ids as cursors in connections

Patch Changes

  • ad8d119b: update dev dependencies

0.17.2

Patch Changes

  • 03aecf76: update .npmignore

0.17.1

Patch Changes

  • c288534e: correctly load type includes when resolving prismaNodes

0.17.0

Minor Changes

  • 4ad5f4ff: Normalize resolveType and isTypeOf behavior to match graphql spec behavior and allow both to be optional

Patch Changes

  • 43ca3031: Update dev dependencies

0.16.3

Patch Changes

  • ab4a9ae4: Fix some type compatibility issues when skipLibCheck is false

0.16.2

Patch Changes

  • 2d9b21cd: Use workspace:* for dev dependencies on pothos packages

0.16.1

Patch Changes

  • b58ee414: Fix primaNode variants

0.16.0

Minor Changes

  • 044396ea: Add support for multiple variants of the same prisma model

0.15.2

Patch Changes

  • a01abb7f: Fix compatability between prisma and auth plugins

0.15.1

Patch Changes

  • ce585cca: fix Prisma object parent shape when combined with other plugins

0.15.0

Minor Changes

  • 4caad5e4: Rename GiraphQL to Pothos

0.14.0

Minor Changes

  • 9307635a: Migrate build process to use turborepo

0.13.3

Patch Changes

  • 2b08f852: Fix syntax highlighting in docs and update npm README.md files"

0.13.2

Patch Changes

  • c6aa732: graphql@15 type compatibility fix

0.13.1

Patch Changes

  • 5619aca: Standardize context caches across all plugins to correctly take advantage of initContextCache

0.13.0

Minor Changes

  • 6d2a6d9: Update to support typescript 4.5. typescript@>4.5.2 is now required for code generation in the prisma plugin

0.12.1

Patch Changes

  • c85dc33: Add types entry in package.json

0.12.0

Minor Changes

  • aeef5e5: Update dependencies

0.11.1

Patch Changes

  • 8e7cb89: remove some debug code

0.11.0

Minor Changes

  • 9107f29: Update dependencies (includes graphql 16)

Patch Changes

  • 53e7905: Correctly pass context to query option of relations and connectedRelations

0.10.0

Minor Changes

  • 17db3bd: Make type refs extendable by plugins

0.9.2

Patch Changes

  • c976bfe: Update dependencies

0.9.1

Patch Changes

  • 4150f92: Fixed esm transformer for path-imports from dependencies

0.9.0

Minor Changes

  • dc87e68: update esm build process so extensions are added during build rather than in source

0.8.2

Patch Changes

  • b4b8381: Updrade deps (typescript 4.4)

0.8.1

Patch Changes

  • 0d655cd: Update README.md

0.8.0

Minor Changes

  • f04be64: #### Breaking

    • The Prisma plugin had been re-designed to use a prisma-generator to generate more efficient types. This requires new additional setup
    • Restored the original API that used model names as strings rather than passing in prisma delegates.

    New

    • Added support for include options on prismaObject and prismaNode types that are automatically loaded. This allows fields defined directly on those types to use nested relations without making additional requests.
    • Added relationCount method to prisma field builder and totalCount option to relatedConnection for more loading of counts.

    Fixed

    • Fixed some bugs related to field nullability
    • Improved include merging to further reduce the number of queries required to resolve a request

Patch Changes

  • f04be64: Update dependencies

0.7.2

Patch Changes

  • cbb4960: Fix priama-connections without relations

0.7.1

Patch Changes

  • 2cf9279: fix for models that do not have any relations

0.7.0

Minor Changes

  • ea4d456: Add interoperability between prisma and errors plugins

0.6.0

Minor Changes

  • 5cdd001: Re-designed how types are propagated in the prisma plugin to improve performance. This requires a few breaking changes to how this plugin is used.

    This change was required because of performance issue in typescript which has been reported here: https://github.com/microsoft/TypeScript/issues/45405

    If this is fixed, the API may be changed back to the slightly nicer string/name based version.

    You will need to remove PrismaClient from the builder types, so your builder setup now looks like:

    import PrismaPlugin, { PrismaTypes } from "@giraphql/plugin-prisma";
    
    export default new SchemaBuilder<{}>({
      prisma: {
        client: prisma,
      },
    });
    

    You will also need to replace model names with the prisma delegates from your prisma client like the following:

    builder.prismaObject(prisma.post, {
      findUnique: (post) => ({ id: post.id }),
      fields: (t) => ({
        id: t.exposeID("id"),
        title: t.exposeString("title"),
        author: t.relation("author"),
      }),
    });
    
    builder.queryType({
      fields: (t) => ({
        me: t.prismaField({
          type: prisma.user,
          resolve: async (query, root, args, ctx, info) =>
            prisma.user.findUniqueOrThrow({
              ...query,
              where: { id: ctx.userId },
            }),
        }),
      }),
    });
    

    See updated docs for more detailed usage.

0.5.0

Minor Changes

  • a4c87cf: Use ".js" extensions everywhere and add module and exports to package.json to better support ems in node

0.4.0

Minor Changes

  • 06e11f9: Pass context to query option of relation and relatedConnection fields

Patch Changes

  • 0d51dcf: Fix nullability of prismaField

0.3.2

Patch Changes

  • ee16577: Fix prisma plugin for multi-word model names.
  • f13208c: bump to fix latest tag

0.3.1

Patch Changes

  • 9ab8fbc: re-release previous version due to build-process issue

0.3.0

Minor Changes

  • 3dd3ff14: Updated dev dependencies, switched to pnpm, and added changesets for releases

All notable changes to this project will be documented in this file. See Conventional Commits for commit guidelines.

0.2.1 - 2021-08-05

📘 Docs

Note: Version bump only for package @giraphql/plugin-prisma

0.2.0 - 2021-08-03

🚀 Updates

  • add relay integration for prisma plugin (e714e54)

🐞 Fixes

  • merge connection args into relatedConnection queries (762c06f)
  • update db seeding to give unique createdAt (279349d)

📘 Docs

  • add docs for prisma relay integration (6c6cbd5)

🛠 Internals

  • update tests with seed data (f3b053a)

Note: Version bump only for package @giraphql/plugin-prisma

0.2.0-alpha.1 - 2021-08-02

🐞 Fixes

  • merge connection args into relatedConnection queries (cd72880)

🛠 Internals

  • update tests with seed data (56fbb7b)

Note: Version bump only for package @giraphql/plugin-prisma

0.2.0-alpha.0 - 2021-08-02

🚀 Updates

  • add relay integration for prisma plugin (0b1d378)

Note: Version bump only for package @giraphql/plugin-prisma

0.1.0 - 2021-07-30

🚀 Updates

Note: Version bump only for package @giraphql/plugin-prisma