Pothos is the plugin-based GraphQL schema builder for TypeScript — define your entire GraphQL API with full type safety, no code generation, no SDL files. Free and open source.
Why Pothos?
The problem with GraphQL in TypeScript: you write SDL strings, then TypeScript types, then resolvers — keeping them in sync is painful. Code generators like GraphQL Code Generator help, but add build steps.
Pothos solves this: your TypeScript code IS the schema. Types flow automatically.
- No code generation — types are inferred from your code
- No SDL files — schema defined in TypeScript
- Plugin system — Prisma, Relay, Auth, Validation, and more
- Works with any server — GraphQL Yoga, Apollo, Mercurius
Quick Start
npm install @pothos/core graphql
import SchemaBuilder from "@pothos/core";
const builder = new SchemaBuilder({});
// Define a type
builder.objectType("User", {
fields: (t) => ({
id: t.exposeID("id"),
name: t.exposeString("name"),
email: t.exposeString("email"),
posts: t.field({
type: ["Post"],
resolve: (user) => db.post.findMany({ where: { authorId: user.id } }),
}),
}),
});
builder.objectType("Post", {
fields: (t) => ({
id: t.exposeID("id"),
title: t.exposeString("title"),
content: t.exposeString("content"),
author: t.field({
type: "User",
resolve: (post) => db.user.findUnique({ where: { id: post.authorId } }),
}),
}),
});
// Define queries
builder.queryType({
fields: (t) => ({
users: t.field({
type: ["User"],
resolve: () => db.user.findMany(),
}),
user: t.field({
type: "User",
nullable: true,
args: { id: t.arg.id({ required: true }) },
resolve: (_, { id }) => db.user.findUnique({ where: { id } }),
}),
}),
});
// Define mutations
builder.mutationType({
fields: (t) => ({
createUser: t.field({
type: "User",
args: {
name: t.arg.string({ required: true }),
email: t.arg.string({ required: true }),
},
resolve: (_, { name, email }) => db.user.create({ data: { name, email } }),
}),
}),
});
// Build the schema
export const schema = builder.toSchema();
Prisma Plugin (Zero-Effort CRUD)
npm install @pothos/plugin-prisma
import SchemaBuilder from "@pothos/core";
import PrismaPlugin from "@pothos/plugin-prisma";
import type PrismaTypes from "@pothos/plugin-prisma/generated";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
const builder = new SchemaBuilder<{
PrismaTypes: PrismaTypes;
}>({
plugins: [PrismaPlugin],
prisma: { client: prisma },
});
// One line per model — Pothos reads your Prisma schema!
builder.prismaObject("User", {
fields: (t) => ({
id: t.exposeID("id"),
name: t.exposeString("name"),
email: t.exposeString("email"),
posts: t.relation("posts"), // Automatic relation
postCount: t.relationCount("posts"), // Automatic count
}),
});
builder.prismaObject("Post", {
fields: (t) => ({
id: t.exposeID("id"),
title: t.exposeString("title"),
author: t.relation("author"),
}),
});
builder.queryType({
fields: (t) => ({
// Automatic Prisma query with type safety
users: t.prismaField({
type: ["User"],
resolve: (query) => prisma.user.findMany({ ...query }),
}),
}),
});
Auth Plugin
import AuthPlugin from "@pothos/plugin-scope-auth";
const builder = new SchemaBuilder<{
Context: { user?: { id: string; role: string } };
AuthScopes: {
logged: boolean;
admin: boolean;
};
}>({
plugins: [AuthPlugin],
authScopes: (ctx) => ({
logged: !!ctx.user,
admin: ctx.user?.role === "admin",
}),
});
// Protected query
builder.queryField("myProfile", (t) =>
t.field({
type: "User",
authScopes: { logged: true },
resolve: (_, __, ctx) => db.user.findUnique({ where: { id: ctx.user!.id } }),
})
);
// Admin-only mutation
builder.mutationField("deleteUser", (t) =>
t.field({
type: "Boolean",
authScopes: { admin: true },
args: { userId: t.arg.id({ required: true }) },
resolve: async (_, { userId }) => {
await db.user.delete({ where: { id: userId } });
return true;
},
})
);
Validation Plugin (Zod Integration)
import ValidationPlugin from "@pothos/plugin-validation";
const builder = new SchemaBuilder({
plugins: [ValidationPlugin],
});
builder.mutationType({
fields: (t) => ({
createUser: t.field({
type: "User",
args: {
name: t.arg.string({ required: true }),
email: t.arg.string({ required: true }),
},
validate: {
// Zod-like validation
name: { minLength: 2, maxLength: 50 },
email: { email: true },
},
resolve: (_, args) => db.user.create({ data: args }),
}),
}),
});
Pothos vs Nexus vs TypeGraphQL vs SDL-first
| Feature | Pothos | Nexus | TypeGraphQL | SDL-first |
|---|---|---|---|---|
| Type safety | Full inference | Full inference | Decorators | Code generation |
| Code generation | None needed | None needed | None needed | Required |
| Prisma integration | Plugin | Plugin | TypeORM | Manual |
| Auth | Plugin | Manual | Decorators | Manual |
| Relay support | Plugin | Plugin | Manual | Manual |
| Actively maintained | Yes (2026) | Slowing | Yes | N/A |
Need to scrape data from any website and get it in structured JSON? Check out my web scraping tools on Apify — no coding required, results in minutes.
Have a custom data extraction project? Email me at spinov001@gmail.com — I build tailored scraping solutions for businesses.
Top comments (0)