prisma-orpc-generator
TypeScript icon, indicating that this package has built-in type declarations

1.1.0 • Public • Published

⚡ Prisma oRPC Generator

Generate typed oRPC routers, Zod schemas, and docs straight from your Prisma schema.

npm CI license node prisma TypeScript Prettier

Prisma v6+, Node 18.18+/20.9+/22.11+, TypeScript ≥ 5.1.

TL;DR — Add the generator to your Prisma schema and run Prisma generate. That’s it.

generator client {
  provider = "prisma-client-js"
}

generator orpc {
  provider = "prisma-orpc-generator"
  output   = "./src/generated/orpc"

  // Optional config (booleans are strings)
  schemaLibrary = "zod"
  generateInputValidation  = "true"
  generateOutputValidation = "true"

  // Shield authorization (optional)
  generateShield = "true"
  defaultReadRule = "allow"
  defaultWriteRule = "auth"
}
# generate
npx prisma generate

📋 Table of Contents


⚡ Quickstart

Click to expand quickstart guide

Prerequisites

  • Node: 18.18.0+, 20.9.0+, or 22.11.0+
  • Prisma CLI (v6+) in your project
  • TypeScript ≥ 5.1.0 recommended

Install

# npm
npm install -D prisma-orpc-generator zod prisma @prisma/client

# pnpm
pnpm add -D prisma-orpc-generator zod prisma @prisma/client

# yarn
yarn add -D prisma-orpc-generator zod prisma @prisma/client

Add the generator (minimal)

generator client {
  provider = "prisma-client-js"
}

generator orpc {
  provider = "prisma-orpc-generator"
  output   = "./src/generated/orpc"
}

Generate

npx prisma generate

🧩 Compatibility

Click to expand compatibility info - Prisma ORM: v6+ - Node.js minimums for Prisma v6: - 18.18.0+ - 20.9.0+ - 22.11.0+ - Not supported: 16, 17, 19, 21 - TypeScript: ≥ 5.1.0

🏗️ What Gets Generated

Click to expand generated files overview A generated surface mirroring your domain:
src/generated/orpc/
├─ routers/
│  ├─ models/           # per-model routers
│  └─ helpers/          # common utilities
├─ tests/               # generated tests
├─ zod-schemas/         # zod (if enabled)
└─ documentation/       # docs (if enabled)

Explore the example outputs:


🛠️ Usage

Click to expand usage guide - Runs as part of Prisma’s generator pipeline. - Default output directory is `./src/generated/orpc` (configurable via the generator block). - Import the generated code into your server/app. See the runnable example server in [examples/basic/src/server.ts](examples/basic/src/server.ts).

Tip: Browse the example’s generated root for real structure: examples/basic/src/generated/orpc.


⚙️ Configuration

Click to expand configuration options Where configuration lives - Inside your generator block in [schema.prisma](examples/basic/schema.prisma) - Booleans are strings: "true"/"false"; numbers as strings are supported

Validated against src/config/schema.ts. Below are the most commonly used options.

Core options

Option Type Default Values Description
output string ./src/generated/orpc Directory for generated oRPC artifacts
schemaLibrary enum "zod" zod Schema validation library
generateInputValidation boolean (string) "true" "true", "false" Emit Zod validation for inputs
generateOutputValidation boolean (string) "true" "true", "false" Emit Zod validation for outputs
strictValidation boolean (string) "true" "true", "false" Stricter Zod shapes for safety
zodSchemasOutputPath string ./zod-schemas Relative path (under output) for Zod files
externalZodImportPath string ./zod-schemas Module/path used when importing Zod schemas
zodDateTimeStrategy enum "coerce" "date", "coerce", "isoString" How DateTime fields are modeled in Zod
zodConfigPath string Path to custom zod.config.json file (relative to schema or absolute)

Operational options

Option Type Default Values Description
generateModelActions string list all see note Comma-separated actions to emit (see note below)
showModelNameInProcedure boolean (string) "true" "true", "false" Prefix procedures with model name
enableSoftDeletes boolean (string) "false" "true", "false" Add soft-delete semantics where applicable
generateRelationResolvers boolean (string) "true" "true", "false" Emit helpers to resolve relations
wrapResponses boolean (string) "false" "true", "false" Wrap handler results in an envelope

DX and formatting

Option Type Default Values Description
useBarrelExports boolean (string) "true" "true", "false" Generate index.ts barrel exports
codeStyle enum "prettier" "prettier", "none" Format generated code with Prettier
generateDocumentation boolean (string) "false" "true", "false" Generate API documentation
generateTests boolean (string) "false" "true", "false" Generate test files
enableDebugLogging boolean (string) "false" "true", "false" Extra logs during generation

Runtime and integration

Option Type Default Values Description
prismaClientPath string @prisma/client Import path for PrismaClient
contextPath string "" Optional path to your app's Context module
serverPort number (string) 3000 Port used by optional docs/server helpers
apiPrefix string "" Prefix used by optional docs/server helpers
apiTitle string Generated API API title for documentation
apiDescription string Auto-generated API from Prisma schema API description for documentation
apiVersion string 1.0.0 API version for documentation

Shield / Authorization

Option Type Default Values Description
generateShield boolean (string) "true" "true", "false" Enable shield generation
shieldPath string Path to custom shield file (absolute, relative to project root, relative to output dir, or module specifier)
defaultReadRule enum "allow" "allow", "deny", "auth" Default rule for read operations
defaultWriteRule enum "auth" "auth", "deny", "allow" Default rule for write operations
denyErrorCode string "FORBIDDEN" Error code for denied access
debug boolean (string) "false" "true", "false" Enable debug logging
allowExternalErrors boolean (string) "false" "true", "false" Allow detailed error messages from shields
Notes
  • generateModelActions supports: create, createMany, findFirst, findFirstOrThrow, findMany, findUnique, findUniqueOrThrow, update, updateMany, upsert, delete, deleteMany, aggregate, groupBy, count, findRaw, aggregateRaw.
  • Booleans are strings in Prisma generator config: use "true" or "false".
  • The full, authoritative shape lives in src/config/schema.ts.
Example: focused configuration with Zod and docs
generator orpc {
  provider = "prisma-orpc-generator"
  output   = "./src/generated/orpc"

  schemaLibrary             = "zod"
  zodDateTimeStrategy       = "coerce"
  generateInputValidation   = "true"
  generateOutputValidation  = "true"
  generateDocumentation     = "true"
  useBarrelExports          = "true"
  codeStyle                 = "prettier"
}

🔧 Zod Schemas Generation

Click to expand zod schemas info

This generator leverages prisma-zod-generator to create Zod schemas from your Prisma models. Here's how the process works:

Generation Process

  1. Automatic Integration: When schemaLibrary = "zod" is set, the generator automatically calls prisma-zod-generator
  2. Configuration Management: Creates a zod.config.json file with optimized settings for oRPC usage
  3. Schema Output: Generates Zod schemas in the zod-schemas/ subdirectory of your output path
  4. Import Integration: Generated oRPC routers automatically import and use these schemas for validation

Configuration File

The generator creates a minimal zod.config.json file:

{
  "mode": "full",
  "output": "./zod-schemas"
}

Additional settings are only added when they differ from defaults:

{
  "mode": "full", 
  "output": "./zod-schemas",
  "dateTimeStrategy": "date"
}

DateTime Handling Strategy

The zodDateTimeStrategy option controls how Prisma DateTime fields are modeled in Zod schemas:

Strategy Zod Schema Description prisma-zod-generator equivalent
"coerce" (default) z.coerce.date() Automatically converts strings/numbers to Date objects dateTimeStrategy: "coerce"
"date" z.date() Requires actual Date objects, no conversion dateTimeStrategy: "date"
"isoString" z.string().regex(ISO).transform() Validates ISO string format, transforms to Date dateTimeStrategy: "isoString"

Custom Zod Configuration

For advanced use cases, you can provide your own zod.config.json:

generator orpc {
  provider = "prisma-orpc-generator"
  output   = "./src/generated/orpc"
  
  zodConfigPath = "./custom-zod.config.json"  // Path to your config file
}

When zodConfigPath is specified:

  • The generator uses your existing configuration
  • oRPC-specific settings are passed as generator options instead of modifying the config file
  • Your custom configuration takes precedence

File Structure

Generated Zod schemas follow this structure:

src/generated/orpc/
├─ zod-schemas/
│  ├─ index.ts           # Barrel exports
│  ├─ objects/           # Model schemas
│  │  ├─ UserSchema.ts
│  │  └─ PostSchema.ts
│  └─ inputTypeSchemas/  # Input validation schemas
│     ├─ UserCreateInput.ts
│     └─ UserUpdateInput.ts
└─ routers/              # oRPC routers (import from ../zod-schemas)

🛡️ Shield Authorization

Click to expand shield authorization guide

The generator can automatically generate orpc-shield configurations for type-safe authorization. Shield provides declarative rules, composable operators, and path-based permissions.

Shield Configuration

Add shield options to your generator config:

generator orpc {
  provider = "prisma-orpc-generator"
  output   = "./src/generated/orpc"

  // Enable shield generation
  generateShield = "true"

  // Option 1: Auto-generate shield rules
  defaultReadRule  = "allow"  // "allow", "deny", "auth"
  defaultWriteRule = "auth"   // "auth", "deny"

  // Option 2: Use custom shield file (relative to output dir)
  // shieldPath = "../auth/my-custom-shield"

  // Error handling
  denyErrorCode = "FORBIDDEN"
  debug = "false"
}

What Gets Generated

Shield generation creates:

src/generated/orpc/
├─ shield.ts              # Shield rules and permissions (auto-generated)
├─ routers/
│  ├─ index.ts           # App router with shield exports
│  └─ helpers/
│     └─ createRouter.ts # Base router with shield middleware integration

When shieldPath is provided: The generator skips auto-generation and dynamically integrates your custom shield file into the generated middleware chain.

Dynamic Shield Path Resolution ✨

The generator now features smart dynamic path resolution for shield files. When you specify a shieldPath, the generator automatically:

  • Resolves relative paths from your project structure
  • Handles different output directory layouts
  • Integrates shield middleware using the proper oRPC pattern
  • Generates correct import paths regardless of nesting depth
  • Applies middleware to all generated procedures through inheritance

Example Generated Integration:

// In src/generated/orpc/routers/helpers/createRouter.ts
import { permissions } from '../../../../custom-shield';
export const or = os.$context<Context>().use(permissions);

Using Custom Shield Files

For advanced use cases, you can provide your own shield file instead of auto-generation:

generator orpc {
  provider = "prisma-orpc-generator"
  output   = "./src/generated/orpc"

  generateShield = "true"
  shieldPath = "../../src/custom-shield"  // Dynamically resolved!
}

Supported Path Formats:

  • Relative paths: "../../src/auth/shield"
  • Project root relative: "src/auth/shield"
  • Absolute paths: "/absolute/path/to/shield"

Your custom shield file should export a permissions object:

// src/custom-shield.ts
import { rule, allow, deny, shield, or } from 'orpc-shield';
import type { Context } from '../generated/orpc/routers/helpers/createRouter';

const isAuthenticated = rule<Context>()(({ ctx }) => !!ctx.user);
const isAdmin = rule<Context>()(({ ctx }) => ctx.user?.role === 'admin');
const isOwner = rule<Context>()(({ ctx, input }) => {
  return ctx.user?.id === (input as any)?.userId;
});

export const permissions = shield<Context>({
  user: {
    userFindMany: allow,           // Match generated procedure names
    userCreate: isAuthenticated,   
    userUpdate: isAuthenticated,
    userDelete: or(isAdmin, isOwner),
    userDeleteMany: deny,          // Explicitly deny dangerous operations
  },
  post: {
    postFindMany: allow,
    postCreate: isAuthenticated,
    postUpdate: isAuthenticated,
    postDelete: isAuthenticated,
  },
}, {
  denyErrorCode: 'FORBIDDEN',      // Maps to HTTP 403
  debug: true,                     // Enable debug logging
  allowExternalErrors: true,       // Allow detailed error messages
});

Important: Shield procedure names should match your generated router names (e.g., userCreate, postFindMany).

Note: When using shieldPath, the generator will skip auto-generation and use your custom shield file instead.

Generated Shield Rules

The generator creates rules based on your Prisma models:

// Built-in rules (generated automatically)
const isAuthenticated = rule<Context>()(({ ctx }) => !!ctx.user);

// Example custom rules (user-defined)
const isAdmin = rule<Context>()(({ ctx }) => ctx.user?.role === 'admin');

// Model-specific rules (generated based on config)
const canReadUser = allow;           // Read operations: allow
const canWriteUser = isAuthenticated; // Write operations: require auth

// Shield configuration
export const permissions = shield<Context>({
  user: {
    list: canReadUser,
    findById: canReadUser,
    create: canWriteUser,
    update: canWriteUser,
    delete: canWriteUser,
  },
  post: {
    list: allow,
    create: isAuthenticated,
    update: isAuthenticated,
  },
});

Using Shield in Your Server

Import and use the generated shield:

import { appRouter, permissions } from './generated/orpc/routers';

// Apply shield at server level
const server = createServer(appRouter, {
  // Shield is applied via middleware
  middleware: [permissions]
});

// Or use with oRPC handlers
import { OpenAPIHandler } from '@orpc/openapi';

const handler = new OpenAPIHandler(appRouter, {
  // Shield permissions are automatically applied
  interceptors: [/* your interceptors */]
});

Context Requirements

Shield rules expect a Context with user information:

interface Context {
  prisma: PrismaClient;
  user?: {
    id: string;
    email?: string;
    name?: string;
    roles?: string[];
    permissions?: string[];
  };
}

Customization

Override default rules by modifying the generated shield.ts:

// Custom rule for post ownership
const isPostOwner = rule<Context>()(({ ctx, input }) => {
  return ctx.user?.id === (input as any)?.authorId;
});

// Use in shield config
const permissions = shield<Context>({
  post: {
    update: and(isAuthenticated, isPostOwner), // Auth + ownership
    delete: or(isAdmin, isPostOwner),          // Admin or owner
  },
});

Shield Options

Option Type Default Values Description
generateShield boolean (string) "true" "true", "false" Enable shield generation
shieldPath string Path to custom shield file (absolute, relative to project root, relative to output dir, or module specifier)
defaultReadRule enum "allow" "allow", "deny", "auth" Default rule for read operations
defaultWriteRule enum "auth" "auth", "deny", "allow" Default rule for write operations
denyErrorCode string "FORBIDDEN" Error code for denied access
debug boolean (string) "false" "true", "false" Enable debug logging
allowExternalErrors boolean (string) "false" "true", "false" Allow detailed error messages from shields

🧪 Examples

Click to expand examples Run the repo example end-to-end ```bash npm run example:basic ```

What it does

  • Builds the generator
  • Generates Prisma artifacts
  • Seeds a local DB
  • Starts a small server using the generated routers/schemas

Notable files


❓ FAQ / Troubleshooting

Click to expand FAQ and troubleshooting Prisma version mismatch - Symptom: generator fails or types not aligned - Action: ensure Prisma v6+ in dev deps and runtime - `npm i -D prisma @prisma/client` - Regenerate: `npx prisma generate`

Node version or ESM issues

  • Symptom: runtime errors about module type or syntax
  • Action: use Node 18.18.0+, 20.9.0+, or 22.11.0+; align package type with your build, then rebuild npm run build

Generated path is unexpected

Schema/config validation failures

  • Symptom: errors referencing invalid options
  • Action: check inputs against src/config/schema.ts; fix paths/booleans; re-run generation

Docs not emitted

Shield path resolution errors

  • Symptom: "Cannot find module" errors for shield imports
  • Action: verify shieldPath points to correct file; check file exports permissions object; ensure path is relative to project root or absolute
  • Note: generator now handles dynamic path resolution automatically for common directory structures

🧑‍💻 Development (Contributing)

Click to expand development guide Repo quicklinks - Source: [src/](src) - Generators: [src/generators/](src/generators) - Entry point: [src/bin.ts](src/bin.ts) - Public exports: [src/index.ts](src/index.ts) - Tests: [tests/](tests)

Local dev loop

npm run dev         # watch build
npm run build       # one-off build
npm run lint        # lint
npm run lint:fix    # lint + fix
npm run format      # prettier
npm run typecheck   # types only

Local development (monorepo) provider example

generator orpc {
  provider = "../../lib/bin.js" // relative path to built generator
  output   = "./src/generated/orpc"
}

Testing

npm test               # unit/integration
npm run test:watch     # watch
npm run test:e2e       # Prisma-backed CRUD
npm run test:coverage  # coverage

Conventions

  • Conventional Commits
  • Ensure npm run build and npm run typecheck pass before PR
  • Update README.md if flags/outputs change

🗺️ Roadmap

  • Integration with oRPC Shield - Built-in authorization with dynamic path resolution
  • Schema-based Auth Configuration - Define authorization rules directly in Prisma schema, JSON, or TypeScript config files
  • Plugin hooks for custom emitters
  • Config discovery and overrides

📄 License

  • License: MIT — see the LICENSE file.
  • Copyright © 2025 Omar Dulaimi.

🙏 Acknowledgements

  • Prisma and its ecosystem
  • oRPC community and patterns
  • Zod for runtime validation
  • TypeScript tooling
  • Vitest and contributors

📝 Changelog

See CHANGELOG.md


Made with ❤️ by Omar Dulaimi

Package Sidebar

Install

npm i prisma-orpc-generator

Weekly Downloads

40

Version

1.1.0

License

MIT

Unpacked Size

330 kB

Total Files

63

Last publish

Collaborators

  • omar-dulaimi