Setup
Add the Pothos generator to your Prisma schema and wire PrismaTypes into the builder.
The Prisma plugin reads a small set of generated types to understand your schema. You add a generator to schema.prisma, run prisma generate, then hand the generated PrismaTypes and a datamodel to the builder. Everything else on these pages assumes this wiring is in place.
npm install --save @pothos/plugin-prismaEarlier versions inferred every type from the Prisma client itself. That made editors slow on large schemas and left some advanced cases impossible to type. The generator exists so the plugin can read a compact, purpose-built set of types instead.
Add the Pothos generator
Add the pothos generator alongside your Prisma client generator. This example uses the Ultimate League schema that runs through every Prisma page:
generator pothos {
provider = "prisma-pothos-types"
}
model Team {
id Int @id @default(autoincrement())
name String @unique
players Player[]
homeGames Game[] @relation("HomeTeam")
awayGames Game[] @relation("AwayTeam")
}
model Player {
id Int @id @default(autoincrement())
name String
number Int
team Team @relation(fields: [teamId], references: [id])
teamId Int
stats PlayerStat[]
}
model Game {
id Int @id @default(autoincrement())
playedAt DateTime
homeTeam Team @relation("HomeTeam", fields: [homeTeamId], references: [id])
homeTeamId Int
awayTeam Team @relation("AwayTeam", fields: [awayTeamId], references: [id])
awayTeamId Int
stats PlayerStat[]
}
model PlayerStat {
id Int @id @default(autoincrement())
goals Int
assists Int
player Player @relation(fields: [playerId], references: [id])
playerId Int
game Game @relation(fields: [gameId], references: [id])
gameId Int
}Pothos types regenerate whenever you regenerate the client:
npx prisma generateTwo generator options control where the types land and what they import from:
output: where to write the generated types file. Defaults next to the Prisma client.clientOutput: the import path the generated file uses to reachPrismaClient. It defaults to the absolute path of wherever the client is generated. If you check the generated file into source control, set this to a relative path so the import survives on other machines.
generator client {
provider = "prisma-client"
output = "../lib/prisma"
}
generator pothos {
provider = "prisma-pothos-types"
clientOutput = "./prisma" // relative path from the pothos output to the client
output = "../lib/pothos-prisma-types.ts"
}If auto-completion for Prisma types and relations is not working, check that the generated types file imports the client from the right location; a stale clientOutput is the usual cause.
Wire up the builder
Register the plugin, give the builder your PrismaTypes, and pass a prisma config with the client and datamodel:
import SchemaBuilder from '@pothos/core';
import { PrismaClient } from '@prisma/client';
import PrismaPlugin from '@pothos/plugin-prisma';
import type PrismaTypes from '../lib/pothos-prisma-types';
import { getDatamodel } from '../lib/pothos-prisma-types';
const prisma = new PrismaClient({});
const builder = new SchemaBuilder<{
// Gives the builder full type information about your Prisma schema.
PrismaTypes: PrismaTypes;
}>({
plugins: [PrismaPlugin],
prisma: {
client: prisma,
// Describes tables, relations, and indexes so Pothos can plan optimal queries at runtime.
dmmf: getDatamodel(),
},
});The rest of the Prisma pages assume this builder and this module-level prisma client already exist.
The datamodel used to ride along on the Prisma client, but most runtimes now strip it to shrink bundle size, so dmmf: getDatamodel() passes it explicitly. getDatamodel is exported from the generated types file.
Prisma config options
The prisma object accepts:
| Option | Purpose |
|---|---|
client | The PrismaClient instance, or a function (ctx) => client to pick a client per request. |
dmmf | The datamodel from getDatamodel(). Required so the plugin can plan queries. |
exposeDescriptions | Use /// comments from the Prisma schema as descriptions for models, relations, and exposed fields. Defaults to false. Pass { models, fields } to enable them selectively; set a field's description to false to opt one out. |
filterConnectionTotalCount | Apply a related connection's where clause to its totalCount. Defaults to true. |
onUnusedQuery | Warn or throw when a resolver forgets to use its query argument. See below. |
maxConnectionSize / defaultConnectionSize | Bounds for Relay connections. |
skipDeferredFragments | Skip @defer fragments when planning the Prisma query, so deferred fields aren't fetched eagerly. Defaults to true. |
A client per request
Pass a function for client to choose a client based on context, useful for periodically recycled clients or read-only replicas for some users:
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),
dmmf: getDatamodel(),
},
});Keep the Prisma client out of your Context type. The client's types are large, and threading them through Context slows type-checking and makes editors laggy (see this TypeScript issue). Reference a module-level prisma singleton instead, as above.
Detecting unused query arguments
t.prismaField and t.prismaConnection hand your resolver a query argument to spread into the Prisma call. Forgetting to spread it produces inefficient queries, or missing data. Set onUnusedQuery to catch the mistake:
'warn'logs a warning when the resolver returns without usingquery.'error'throws instead.- A function receives the
infoobject so you can log or throw your own error.
prisma: {
client: prisma,
dmmf: getDatamodel(),
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn',
}The check is deliberately naive: it wraps the query object's properties in getters that flip a flag when read. If nothing on the object is accessed before the resolver returns, the onUnusedQuery condition fires. Enable it in development to surface these issues quickly.