Prisma 2+ generator to emit fully implemented tRPC routers
739
stars
382
commits
TypeScript
primary language
Jul 28, 2026
updated
Automatically generate fully implemented, type-safe tRPC routers from your Prisma schema.
🎯 Zero‑config • 🛡️ Type‑safe • ⚡ Fast • 🔧 Customizable
Transforms your Prisma schema into production‑ready tRPC APIs with Zod validation, middleware, and optional tRPC Shield.
| Component | Minimum | Recommended |
|---|---|---|
| Node.js | 20.19.0 | 22.x |
| Prisma | 7.0.0 | Latest 7.x |
| TypeScript | 5.4.0 | 5.9.x |
# npm
npm install prisma-trpc-generator
# yarn
yarn add prisma-trpc-generator
# pnpm
pnpm add prisma-trpc-generator
Create prisma.config.ts at the repo root:
import 'dotenv/config';
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
seed: 'tsx prisma/seed.ts',
},
datasource: {
url: env('DATABASE_URL'),
},
});
Update your generator client block:
generator client {
provider = "prisma-client"
output = "../node_modules/.prisma/client"
}
Set DATABASE_URL (e.g., file:./prisma/dev.db) in .env and instantiate PrismaClient with the adapter that matches your database (SQLite → @prisma/adapter-better-sqlite3, Postgres → @prisma/adapter-pg, etc.).
Add the generator to your Prisma schema and point to your JSON config file:
generator trpc {
provider = "prisma-trpc-generator"
output = "./generated"
config = "./trpc.config.json"
}
Both output and config are resolved relative to the schema file, the way Prisma resolves
generator paths. With the schema at prisma/schema.prisma, the block above reads
prisma/trpc.config.json and writes to prisma/generated.
Create that config file (see Feature guide for options), enable "strict": true in tsconfig.json, then generate:
npx prisma generate
As of v2.x, configuration is unified via a single JSON file. Your Prisma generator block should only specify output and config.
Example prisma/trpc.config.json:
{
"withZod": true,
"withMiddleware": true,
"withShield": "./shield",
"contextPath": "./context",
"trpcOptionsPath": "./trpcOptions",
"dateTimeStrategy": "date",
"withMeta": false,
"postman": true,
"postmanExamples": "skeleton",
"openapi": true,
"withRequestId": false,
"withLogging": false,
"withServices": false,
"showModelNameInProcedure": true
}
Notes
showModelNameInProcedure controls whether the model name is appended to each
procedure. It defaults to true, giving createOneUser and aggregateUser.
Set it to false for createOne and aggregate, which reads better when the
procedures already sit under a per-model router.configPath and configFile are also accepted.Each feature is opt‑in via the JSON config. Below are concise how‑tos and the exact keys to set.
withZod: trueschemas/ with Zod types for procedure inputs; routers wire .input() automatically.dateTimeStrategy to control DateTime field validation:
"date" (default): z.date() - accepts only Date objects"coerce": z.coerce.date() - accepts both Date objects and ISO strings"isoString": ISO string validation with transformationYou can add additional Zod validation constraints using special comments in your Prisma schema:
model User {
id Int @id @default(autoincrement()) /// @zod.number.int()
email String @unique /// @zod.string.email()
name String? /// @zod.string.min(1).max(100)
age Int? /// @zod.number.int().min(0).max(120)
posts Post[]
}
model Post {
id Int @id @default(autoincrement()) /// @zod.number.int()
title String /// @zod.string.min(1).max(255, { message: "Title must be shorter than 256 characters" })
content String? /// @zod.string.max(10000)
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
This generates Zod schemas with the specified validations:
export const UserCreateInput = z.object({
id: z.number().int(),
email: z.string().email(),
name: z.string().min(1).max(100).nullish(),
age: z.number().int().min(0).max(120).nullish(),
// ...
});
For more advanced Zod validation options and syntax, see the prisma-zod-generator documentation.
withMiddleware: boolean | string, withShield: boolean | stringwithMiddleware: true, a basic middleware scaffold is included; or point to your own path string.withShield is truthy, the generator imports your permissions and exposes shieldedProcedure in createRouter.ts.auth: boolean | { strategy?: 'session'|'jwt'|'custom'; rolesField?: string; jwt?: {...}; session?: {...}; custom?: {...} }routers/helpers/auth-strategy.ts (stubs + default HS256 JWT verifier)routers/helpers/auth.ts with ensureAuth and ensureRolecreateRouter.ts wires authMiddleware, publicProcedure, protectedProcedure, roleProcedure(roles)docs/usage/auth.md for strategy hooks and examples.withRequestId: boolean, withLogging: booleantrpcOptions.errorFormatter.withMeta: boolean | { openapi?: boolean; auth?: boolean; description?: boolean; defaultMeta?: object }.meta() calls to generated procedures with:
defaultMeta configurationopenapi: boolean | { enabled?: boolean; title?: string; version?: string; baseUrl?: string; pathPrefix?: string; pathStyle?: 'slash'|'dot'; includeExamples?: boolean }openapi/openapi.json and routers/adapters/openapi.ts with a tagged document.{ input: {} } request body schema and optional skeleton examples.postman: boolean | { endpoint?: string; envName?: string; fromOpenApi?: boolean; examples?: 'none'|'skeleton' }postman/collection.json. When fromOpenApi: true, the collection is derived from OpenAPI.examples: 'skeleton' to include sample bodies for common operations.withServices, serviceStyle, serviceDir, withListMethod, serviceImportsprisma/trpc.config.json and move all previous inline keys into it.generator trpc so it only contains output and config.For the following schema:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
content String?
published Boolean @default(false)
viewCount Int @default(0)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
The generator creates:

generated/
├── routers/
│ ├── index.ts # Main app router combining all model routers
│ ├── helpers/
│ │ └── createRouter.ts # Base router factory with middleware/shield setup
│ ├── User.router.ts # User CRUD operations
│ └── Post.router.ts # Post CRUD operations
└── schemas/ # Zod validation schemas (if withZod: true)
├── objects/ # Input type schemas
├── findManyUser.schema.ts
├── createOneUser.schema.ts
└── index.ts # Barrel exports
// src/middleware.ts
import { TRPCError } from '@trpc/server';
import { t } from './trpc';
export const authMiddleware = t.middleware(async ({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({
ctx: {
...ctx,
user: ctx.user,
},
});
});
export const loggingMiddleware = t.middleware(async ({ path, type, next }) => {
console.log(`tRPC ${type} ${path}`);
return next();
});
// src/permissions.ts
import { shield, rule, and } from 'trpc-shield';
const isAuthenticated = rule()(async (_parent, _args, ctx) => !!ctx.user);
const isOwner = rule()(async (_parent, args, ctx) => {
if (!args.where?.id) return false;
const post = await ctx.prisma.post.findUnique({
where: { id: args.where.id },
select: { authorId: true },
});
return post?.authorId === ctx.user?.id;
});
export const permissions = shield({
query: {
findManyPost: true, // Public
findUniqueUser: isAuthenticated,
},
mutation: {
createOnePost: isAuthenticated,
updateOnePost: and(isAuthenticated, isOwner),
deleteOnePost: and(isAuthenticated, isOwner),
},
});
// src/trpcOptions.ts
import { ZodError } from 'zod';
import superjson from 'superjson';
export default {
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.code === 'BAD_REQUEST' && error.cause instanceof ZodError
? error.cause.flatten()
: null,
},
};
},
};
/// @@Gen.model(hide: true)
model InternalLog {
id Int @id @default(autoincrement())
message String
createdAt DateTime @default(now())
}
// src/context.ts
import 'dotenv/config';
import { PrismaClient } from '@prisma/client';
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3';
const adapter = new PrismaBetterSqlite3({
url: process.env.DATABASE_URL as ':memory:' | (string & {}),
});
const prisma = new PrismaClient({ adapter });
export interface Context {
prisma: PrismaClient;
user?: { id: string; email: string; role: string };
}
export const createContext = async ({ req }): Promise<Context> => {
const user = await getUserFromRequest(req);
return { prisma, user };
};
// src/server/routers/posts.ts
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
export const postsRouter = createTRPCRouter({
getAll: publicProcedure.query(({ ctx }) =>
ctx.prisma.post.findMany({
where: { published: true },
include: { author: { select: { name: true } } },
}),
),
create: protectedProcedure
.input(
z.object({ title: z.string().min(1), content: z.string().optional() }),
)
.mutation(({ ctx, input }) =>
ctx.prisma.post.create({ data: { ...input, authorId: ctx.user.id } }),
),
update: protectedProcedure
.input(
z.object({
id: z.number(),
title: z.string().min(1).optional(),
content: z.string().optional(),
}),
)
.mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
const post = await ctx.prisma.post.findFirst({
where: { id, authorId: ctx.user.id },
});
if (!post) throw new TRPCError({ code: 'FORBIDDEN' });
return ctx.prisma.post.update({ where: { id }, data });
}),
});
// src/app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/api/root';
import { createContext } from '@/server/api/context';
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext,
});
export { handler as GET, handler as POST };
// src/lib/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/api/root';
export const trpc = createTRPCReact<AppRouter>();
const PostList = () => {
const { data: posts, isLoading } = trpc.post.findMany.useQuery();
const createPost = trpc.post.createOne.useMutation();
if (isLoading) return <div>Loading...</div>;
return (
<div>
{posts?.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
};
Error: Cannot find module '../context'
contextPath is correct relative to the output directory.Context type.TypeScript errors in generated routers
strict: true is enabled in tsconfig.json.Generated routers not updating
npx prisma generate after modifying your schema.schema.prisma.Zod validation errors
dateTimeStrategy: "coerce" to accept date strings.For large schemas (50+ models):
Build times:
.gitignore.Q: Can I customize the generated router validation rules? A: Routers are generated based on your Prisma schema constraints; change your Prisma model definitions to affect validation.
Q: Does this work with Prisma Edge Runtime? A: Yes.
Q: What databases are supported? A: All Prisma‑compatible databases.
Q: How are enums handled? A: Enums are converted to Zod enums and included in validation.
Q: Can I exclude fields from validation?
A: Use Prisma's @ignore or @@Gen.model(hide: true).
git clone https://github.com/your-username/prisma-trpc-generator.git
cd prisma-trpc-generator
pnpm install
.envprisma.config.ts reads DATABASE_URL, and .env is not committed, so a fresh clone has to
create it before anything can run Prisma.
cp .env.example .env
pnpm run generate
pnpm test
The suite lives in tests/ and is organised by feature: routers, Zod schemas, middleware and
shield, auth, OpenAPI, Postman, tenancy and soft delete, and custom client paths. Every one of
them generates from a real Prisma schema and asserts against the emitted files, so pnpm run generate
must have run first.
Run specific test suites
# everything, quietly
pnpm test --silent
# one file
pnpm test tests/feature-zod-schemas.test.ts
# with coverage (a Vitest flag; there is no separate coverage script)
pnpm test --coverage
Coverage of src/ reads 0 because each suite shells out to prisma generate, which runs the
generator in a child process that the coverage provider does not instrument. The numbers to read
are the ones for the generated routers.
The suites above run the built lib/ from inside this repo, where node_modules already holds
every devDependency, so they pass even when the published manifest under-declares its runtime
dependencies. This packs the tarball, installs it into an empty project outside the repo, and
loads the generator from there:
pnpm run test:packaging
pnpm run lint
pnpm run format
Semantic versioning
This project is licensed under the MIT License. See the LICENSE file for details.
Made with ❤️ by
Omar Dulaimi
⚡ Accelerating tRPC development, one schema at a time
TypeScript
87.1%
JavaScript
12.3%
Prisma 2+ generator to emit fully implemented tRPC routers
739
stars
382
commits
TypeScript
primary language
Jul 28, 2026
updated
Automatically generate fully implemented, type-safe tRPC routers from your Prisma schema.
🎯 Zero‑config • 🛡️ Type‑safe • ⚡ Fast • 🔧 Customizable
Transforms your Prisma schema into production‑ready tRPC APIs with Zod validation, middleware, and optional tRPC Shield.
| Component | Minimum | Recommended |
|---|---|---|
| Node.js | 20.19.0 | 22.x |
| Prisma | 7.0.0 | Latest 7.x |
| TypeScript | 5.4.0 | 5.9.x |
# npm
npm install prisma-trpc-generator
# yarn
yarn add prisma-trpc-generator
# pnpm
pnpm add prisma-trpc-generator
Create prisma.config.ts at the repo root:
import 'dotenv/config';
import { defineConfig, env } from 'prisma/config';
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations',
seed: 'tsx prisma/seed.ts',
},
datasource: {
url: env('DATABASE_URL'),
},
});
Update your generator client block:
generator client {
provider = "prisma-client"
output = "../node_modules/.prisma/client"
}
Set DATABASE_URL (e.g., file:./prisma/dev.db) in .env and instantiate PrismaClient with the adapter that matches your database (SQLite → @prisma/adapter-better-sqlite3, Postgres → @prisma/adapter-pg, etc.).
Add the generator to your Prisma schema and point to your JSON config file:
generator trpc {
provider = "prisma-trpc-generator"
output = "./generated"
config = "./trpc.config.json"
}
Both output and config are resolved relative to the schema file, the way Prisma resolves
generator paths. With the schema at prisma/schema.prisma, the block above reads
prisma/trpc.config.json and writes to prisma/generated.
Create that config file (see Feature guide for options), enable "strict": true in tsconfig.json, then generate:
npx prisma generate
As of v2.x, configuration is unified via a single JSON file. Your Prisma generator block should only specify output and config.
Example prisma/trpc.config.json:
{
"withZod": true,
"withMiddleware": true,
"withShield": "./shield",
"contextPath": "./context",
"trpcOptionsPath": "./trpcOptions",
"dateTimeStrategy": "date",
"withMeta": false,
"postman": true,
"postmanExamples": "skeleton",
"openapi": true,
"withRequestId": false,
"withLogging": false,
"withServices": false,
"showModelNameInProcedure": true
}
Notes
showModelNameInProcedure controls whether the model name is appended to each
procedure. It defaults to true, giving createOneUser and aggregateUser.
Set it to false for createOne and aggregate, which reads better when the
procedures already sit under a per-model router.configPath and configFile are also accepted.Each feature is opt‑in via the JSON config. Below are concise how‑tos and the exact keys to set.
withZod: trueschemas/ with Zod types for procedure inputs; routers wire .input() automatically.dateTimeStrategy to control DateTime field validation:
"date" (default): z.date() - accepts only Date objects"coerce": z.coerce.date() - accepts both Date objects and ISO strings"isoString": ISO string validation with transformationYou can add additional Zod validation constraints using special comments in your Prisma schema:
model User {
id Int @id @default(autoincrement()) /// @zod.number.int()
email String @unique /// @zod.string.email()
name String? /// @zod.string.min(1).max(100)
age Int? /// @zod.number.int().min(0).max(120)
posts Post[]
}
model Post {
id Int @id @default(autoincrement()) /// @zod.number.int()
title String /// @zod.string.min(1).max(255, { message: "Title must be shorter than 256 characters" })
content String? /// @zod.string.max(10000)
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
This generates Zod schemas with the specified validations:
export const UserCreateInput = z.object({
id: z.number().int(),
email: z.string().email(),
name: z.string().min(1).max(100).nullish(),
age: z.number().int().min(0).max(120).nullish(),
// ...
});
For more advanced Zod validation options and syntax, see the prisma-zod-generator documentation.
withMiddleware: boolean | string, withShield: boolean | stringwithMiddleware: true, a basic middleware scaffold is included; or point to your own path string.withShield is truthy, the generator imports your permissions and exposes shieldedProcedure in createRouter.ts.auth: boolean | { strategy?: 'session'|'jwt'|'custom'; rolesField?: string; jwt?: {...}; session?: {...}; custom?: {...} }routers/helpers/auth-strategy.ts (stubs + default HS256 JWT verifier)routers/helpers/auth.ts with ensureAuth and ensureRolecreateRouter.ts wires authMiddleware, publicProcedure, protectedProcedure, roleProcedure(roles)docs/usage/auth.md for strategy hooks and examples.withRequestId: boolean, withLogging: booleantrpcOptions.errorFormatter.withMeta: boolean | { openapi?: boolean; auth?: boolean; description?: boolean; defaultMeta?: object }.meta() calls to generated procedures with:
defaultMeta configurationopenapi: boolean | { enabled?: boolean; title?: string; version?: string; baseUrl?: string; pathPrefix?: string; pathStyle?: 'slash'|'dot'; includeExamples?: boolean }openapi/openapi.json and routers/adapters/openapi.ts with a tagged document.{ input: {} } request body schema and optional skeleton examples.postman: boolean | { endpoint?: string; envName?: string; fromOpenApi?: boolean; examples?: 'none'|'skeleton' }postman/collection.json. When fromOpenApi: true, the collection is derived from OpenAPI.examples: 'skeleton' to include sample bodies for common operations.withServices, serviceStyle, serviceDir, withListMethod, serviceImportsprisma/trpc.config.json and move all previous inline keys into it.generator trpc so it only contains output and config.For the following schema:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String
content String?
published Boolean @default(false)
viewCount Int @default(0)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
The generator creates:

generated/
├── routers/
│ ├── index.ts # Main app router combining all model routers
│ ├── helpers/
│ │ └── createRouter.ts # Base router factory with middleware/shield setup
│ ├── User.router.ts # User CRUD operations
│ └── Post.router.ts # Post CRUD operations
└── schemas/ # Zod validation schemas (if withZod: true)
├── objects/ # Input type schemas
├── findManyUser.schema.ts
├── createOneUser.schema.ts
└── index.ts # Barrel exports
// src/middleware.ts
import { TRPCError } from '@trpc/server';
import { t } from './trpc';
export const authMiddleware = t.middleware(async ({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({
ctx: {
...ctx,
user: ctx.user,
},
});
});
export const loggingMiddleware = t.middleware(async ({ path, type, next }) => {
console.log(`tRPC ${type} ${path}`);
return next();
});
// src/permissions.ts
import { shield, rule, and } from 'trpc-shield';
const isAuthenticated = rule()(async (_parent, _args, ctx) => !!ctx.user);
const isOwner = rule()(async (_parent, args, ctx) => {
if (!args.where?.id) return false;
const post = await ctx.prisma.post.findUnique({
where: { id: args.where.id },
select: { authorId: true },
});
return post?.authorId === ctx.user?.id;
});
export const permissions = shield({
query: {
findManyPost: true, // Public
findUniqueUser: isAuthenticated,
},
mutation: {
createOnePost: isAuthenticated,
updateOnePost: and(isAuthenticated, isOwner),
deleteOnePost: and(isAuthenticated, isOwner),
},
});
// src/trpcOptions.ts
import { ZodError } from 'zod';
import superjson from 'superjson';
export default {
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.code === 'BAD_REQUEST' && error.cause instanceof ZodError
? error.cause.flatten()
: null,
},
};
},
};
/// @@Gen.model(hide: true)
model InternalLog {
id Int @id @default(autoincrement())
message String
createdAt DateTime @default(now())
}
// src/context.ts
import 'dotenv/config';
import { PrismaClient } from '@prisma/client';
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3';
const adapter = new PrismaBetterSqlite3({
url: process.env.DATABASE_URL as ':memory:' | (string & {}),
});
const prisma = new PrismaClient({ adapter });
export interface Context {
prisma: PrismaClient;
user?: { id: string; email: string; role: string };
}
export const createContext = async ({ req }): Promise<Context> => {
const user = await getUserFromRequest(req);
return { prisma, user };
};
// src/server/routers/posts.ts
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
export const postsRouter = createTRPCRouter({
getAll: publicProcedure.query(({ ctx }) =>
ctx.prisma.post.findMany({
where: { published: true },
include: { author: { select: { name: true } } },
}),
),
create: protectedProcedure
.input(
z.object({ title: z.string().min(1), content: z.string().optional() }),
)
.mutation(({ ctx, input }) =>
ctx.prisma.post.create({ data: { ...input, authorId: ctx.user.id } }),
),
update: protectedProcedure
.input(
z.object({
id: z.number(),
title: z.string().min(1).optional(),
content: z.string().optional(),
}),
)
.mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
const post = await ctx.prisma.post.findFirst({
where: { id, authorId: ctx.user.id },
});
if (!post) throw new TRPCError({ code: 'FORBIDDEN' });
return ctx.prisma.post.update({ where: { id }, data });
}),
});
// src/app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/api/root';
import { createContext } from '@/server/api/context';
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext,
});
export { handler as GET, handler as POST };
// src/lib/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/api/root';
export const trpc = createTRPCReact<AppRouter>();
const PostList = () => {
const { data: posts, isLoading } = trpc.post.findMany.useQuery();
const createPost = trpc.post.createOne.useMutation();
if (isLoading) return <div>Loading...</div>;
return (
<div>
{posts?.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
);
};
Error: Cannot find module '../context'
contextPath is correct relative to the output directory.Context type.TypeScript errors in generated routers
strict: true is enabled in tsconfig.json.Generated routers not updating
npx prisma generate after modifying your schema.schema.prisma.Zod validation errors
dateTimeStrategy: "coerce" to accept date strings.For large schemas (50+ models):
Build times:
.gitignore.Q: Can I customize the generated router validation rules? A: Routers are generated based on your Prisma schema constraints; change your Prisma model definitions to affect validation.
Q: Does this work with Prisma Edge Runtime? A: Yes.
Q: What databases are supported? A: All Prisma‑compatible databases.
Q: How are enums handled? A: Enums are converted to Zod enums and included in validation.
Q: Can I exclude fields from validation?
A: Use Prisma's @ignore or @@Gen.model(hide: true).
git clone https://github.com/your-username/prisma-trpc-generator.git
cd prisma-trpc-generator
pnpm install
.envprisma.config.ts reads DATABASE_URL, and .env is not committed, so a fresh clone has to
create it before anything can run Prisma.
cp .env.example .env
pnpm run generate
pnpm test
The suite lives in tests/ and is organised by feature: routers, Zod schemas, middleware and
shield, auth, OpenAPI, Postman, tenancy and soft delete, and custom client paths. Every one of
them generates from a real Prisma schema and asserts against the emitted files, so pnpm run generate
must have run first.
Run specific test suites
# everything, quietly
pnpm test --silent
# one file
pnpm test tests/feature-zod-schemas.test.ts
# with coverage (a Vitest flag; there is no separate coverage script)
pnpm test --coverage
Coverage of src/ reads 0 because each suite shells out to prisma generate, which runs the
generator in a child process that the coverage provider does not instrument. The numbers to read
are the ones for the generated routers.
The suites above run the built lib/ from inside this repo, where node_modules already holds
every devDependency, so they pass even when the published manifest under-declares its runtime
dependencies. This packs the tarball, installs it into an empty project outside the repo, and
loads the generator from there:
pnpm run test:packaging
pnpm run lint
pnpm run format
Semantic versioning
This project is licensed under the MIT License. See the LICENSE file for details.
Made with ❤️ by
Omar Dulaimi
⚡ Accelerating tRPC development, one schema at a time
TypeScript
87.1%
JavaScript
12.3%