- Define object types with `builder.objectType('User', { fields: (t) => ({ id: t.exposeID('id'), name: t.exposeString('name') }) })` — use `expose*` helpers for direct field mapping.
- Register queries via `builder.queryField('user', (t) => t.field({ type: 'User', args: { id: t.arg.id({ required: true }) }, resolve: (root, args, ctx) => ctx.db.findUser(args.id) }))` — keep resolvers thin and delegate to service layer.
- Use `builder.mutationField` with `input` arg types for mutations: `t.arg({ type: CreateUserInput, required: true })`.
- Use lazy type references (`() => UserType`) to break circular dependencies between interconnected types.
- Enable `@pothos/plugin-prisma` for zero-config Prisma integration: `builder.prismaObject('User', { fields: (t) => ({ posts: t.relation('posts') }) })`.
- Define object types with `builder.objectType('User', { fields: (t) => ({ id: t.exposeID('id'), name: t.exposeString('name'), email: t.exposeString('email') }) })` — use `expose*` helpers (`exposeID`, `exposeString`, `exposeInt`, `exposeBoolean`) to map database fields without boilerplate.
- Register query fields via `builder.queryField('user', (t) => t.field({ type: 'User', nullable: true, args: { id: t.arg.id({ required: true }) }, resolve: (root, args, ctx) => ctx.db.user.findUnique({ where: { id: args.id } }) }))` — keep resolvers thin.
- Define mutations with a dedicated input type: `const CreateUserInput = builder.inputType('CreateUserInput', { fields: (t) => ({ name: t.string({ required: true }), email: t.string({ required: true }) }) })`.
- Use `builder.enumType(Role, { name: 'Role' })` to expose TypeScript enums to the GraphQL schema — do not inline string literals.
- Use `t.field({ type: [Post], resolve: ... })` for list fields and `t.field({ type: Post, nullable: true })` for optional fields — never use raw strings for custom types.
- Prevent N+1 with `@pothos/plugin-dataloader`: `t.loadableField({ type: User, load: (ids, ctx) => ctx.db.users.findMany({ where: { id: { in: ids } } }), resolve: (parent) => parent.userId })`.
- Use lazy references (`() => UserType`) when two types reference each other to avoid initialization order issues.