npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

prisma-trpc-generator

v3.0.1

Published

Prisma 2+ generator to emit fully implemented tRPC routers

Readme

⚡ Prisma tRPC Generator

Automatically generate fully implemented, type-safe tRPC routers from your Prisma schema.

🎯 Zero‑config • 🛡️ Type‑safe • ⚡ Fast • 🔧 Customizable

✨ Key features

  • 🚀 Zero configuration defaults
  • 🔄 Always in sync with your Prisma schema
  • 🛡️ 100% TypeScript type-safety
  • 🎯 Complete CRUD coverage for all Prisma operations
  • ⚙️ Highly configurable: paths, middleware, shield, options
  • 📦 Lightweight and fast generation
  • 🔗 Integrates with Zod, tRPC Shield, and custom middleware

📚 Table of contents


🚀 Quick start

Requirements

| Component | Minimum | Recommended | | ---------- | ------- | ----------- | | Node.js | 20.19.0 | 22.x | | Prisma | 7.0.0 | Latest 7.x | | TypeScript | 5.4.0 | 5.9.x |

Install

# npm
npm install prisma-trpc-generator

# yarn
yarn add prisma-trpc-generator

# pnpm
pnpm add prisma-trpc-generator

Configure Prisma 7

  1. 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'),
      },
    });
  2. Update your generator client block:

    generator client {
      provider = "prisma-client"
      output   = "../node_modules/.prisma/client"
    }
  3. 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.).

Minimal setup

Add the generator to your Prisma schema and point to your JSON config file:

generator trpc {
  provider = "node ./lib/generator.js"
  output   = "./prisma/generated"
  config   = "./prisma/trpc.config.json"
}

Create prisma/trpc.config.json (see Feature guide for options), enable "strict": true in tsconfig.json, then generate:

npx prisma generate

⚙️ Configuration

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
}

Notes

  • The config path is resolved relative to the Prisma schema file.
  • Aliases configPath and configFile are also accepted.
  • If a config file is provided, any inline options in the generator block are ignored with a warning.
  • Inline options without a config file still work for now but are deprecated and will be removed in a future major release.

🔎 Feature guide

Each feature is opt‑in via the JSON config. Below are concise how‑tos and the exact keys to set.

1) Zod validation (inputs)

  • Key: withZod: true
  • Emits schemas/ with Zod types for procedure inputs; routers wire .input() automatically.
  • Date handling: Set 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 transformation

Extending Zod schemas with Prisma comments

You 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.

2) Middleware & Shield

  • Keys: withMiddleware: boolean | string, withShield: boolean | string
  • When withMiddleware: true, a basic middleware scaffold is included; or point to your own path string.
  • When withShield is truthy, the generator imports your permissions and exposes shieldedProcedure in createRouter.ts.

3) Auth (session / JWT / custom)

  • Key: auth: boolean | { strategy?: 'session'|'jwt'|'custom'; rolesField?: string; jwt?: {...}; session?: {...}; custom?: {...} }
  • When enabled, generator emits:
    • routers/helpers/auth-strategy.ts (stubs + default HS256 JWT verifier)
    • routers/helpers/auth.ts with ensureAuth and ensureRole
    • createRouter.ts wires authMiddleware, publicProcedure, protectedProcedure, roleProcedure(roles)
  • See docs/usage/auth.md for strategy hooks and examples.

4) Request ID + logging

  • Keys: withRequestId: boolean, withLogging: boolean
  • Adds a small requestId middleware and optional structured log line around every procedure.
  • To propagate requestId into errors, return it in your trpcOptions.errorFormatter.

5) tRPC Metadata Support

  • Key: withMeta: boolean | { openapi?: boolean; auth?: boolean; description?: boolean; defaultMeta?: object }
  • When enabled, adds .meta() calls to generated procedures with:
    • OpenAPI-compatible metadata (HTTP methods, paths, tags, descriptions)
    • Authentication metadata for middleware integration
    • Custom metadata via defaultMeta configuration
  • Perfect for OpenAPI documentation, conditional auth, and enhanced middleware

6) OpenAPI (MVP)

  • Key: openapi: boolean | { enabled?: boolean; title?: string; version?: string; baseUrl?: string; pathPrefix?: string; pathStyle?: 'slash'|'dot'; includeExamples?: boolean }
  • Emits openapi/openapi.json and routers/adapters/openapi.ts with a tagged document.
  • Paths map to tRPC endpoints (POST) with a { input: {} } request body schema and optional skeleton examples.

7) Postman collection

  • Key: postman: boolean | { endpoint?: string; envName?: string; fromOpenApi?: boolean; examples?: 'none'|'skeleton' }
  • Emits postman/collection.json. When fromOpenApi: true, the collection is derived from OpenAPI.
  • Set examples: 'skeleton' to include sample bodies for common operations.

8) DDD services (optional)

  • Keys: withServices, serviceStyle, serviceDir, withListMethod, serviceImports
  • Emits a BaseService and per‑model service stubs; routers can delegate to services when enabled.
  • Tenancy/soft‑delete helpers are included in the service layer if you choose to use it.

Migration from inline config

  1. Create prisma/trpc.config.json and move all previous inline keys into it.
  2. Replace keys in generator trpc so it only contains output and config.
  3. Run generation. If you still have inline keys, the generator will ignore them and warn.

📋 Generated output

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:

tRPC Routers

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

🛠️ Advanced usage

Custom middleware

// 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();
});

Integration with tRPC Shield

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

Custom tRPC options

// 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,
      },
    };
  },
};

🎨 Customizations

Skipping models

/// @@Gen.model(hide: true)
model InternalLog {
  id        Int      @id @default(autoincrement())
  message   String
  createdAt DateTime @default(now())
}

Custom context

// 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 };
};

📚 Examples

Basic CRUD with authentication

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

Next.js App Router integration

// 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 };

Client-side usage

// 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>
  );
};

🔍 Troubleshooting, performance, FAQ

Common issues

Error: Cannot find module '../context'

  • Ensure your contextPath is correct relative to the output directory.
  • Check that your context file exports a Context type.

TypeScript errors in generated routers

  • Ensure dependencies are installed and up to date.
  • Verify your tRPC context is properly typed.
  • Ensure strict: true is enabled in tsconfig.json.

Generated routers not updating

  • Run npx prisma generate after modifying your schema.
  • Check that the generator is properly configured in schema.prisma.
  • Clear your build cache and regenerate.

Zod validation errors

  • Ensure Zod 4.0+ is installed.
  • Check that input schemas match your Prisma model types.
  • For DateTime validation errors with JSON APIs, set dateTimeStrategy: "coerce" to accept date strings.

Performance considerations

For large schemas (50+ models):

  • Use selective generation with model hiding.
  • Split routers into multiple files.
  • Consider lazy loading routers.

Build times:

  • Add generated files to .gitignore.
  • Use parallel builds where possible.
  • Cache dependencies in CI.

FAQ

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).

Getting help

  • 🐛 Bug reports: https://github.com/omar-dulaimi/prisma-trpc-generator/issues/new
  • 💡 Feature requests: https://github.com/omar-dulaimi/prisma-trpc-generator/issues/new
  • 💬 Discussions: https://github.com/omar-dulaimi/prisma-trpc-generator/discussions

🤝 Contributing

Development setup

  1. Fork and clone the repository
git clone https://github.com/your-username/prisma-trpc-generator.git
cd prisma-trpc-generator
  1. Install dependencies (requires Node.js 20.19.0+; 22.x recommended, this repo uses pnpm)
pnpm install
  1. Build/generate
pnpm run generate
  1. Run tests
pnpm test

Testing

  • Unit tests: core transformation logic
  • Integration tests: end‑to‑end router generation
  • Multi‑provider tests: all database providers
  • Performance tests: large schema handling

Run specific test suites

pnpm test --silent
pnpm run test:integration
pnpm run test:coverage
pnpm run test:comprehensive

Contribution guidelines

  1. Create an issue for bugs or feature requests.
  2. Follow the existing code style (ESLint + Prettier).
  3. Add tests for new functionality.
  4. Update documentation as needed.
  5. Submit a PR with a clear description.

Code style

pnpm run lint
pnpm run format

Release process

Semantic versioning

  • Patch: bug fixes and small improvements
  • Minor: new features and enhancements
  • Major: breaking changes

📄 License

This project is licensed under the MIT License — see the LICENSE file for details.

🔗 Related projects

🙏 Acknowledgments

  • Prisma — Modern database toolkit
  • tRPC — End‑to‑end typesafe APIs
  • Zod — TypeScript‑first schema validation
  • All contributors