Generate typed oRPC routers, Zod schemas, and docs straight from your Prisma schema.
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
- ⚡ Quickstart - Get up and running in minutes
- 🏗️ What Gets Generated - See what files are created
- ⚙️ Configuration - All available options
- 🔧 Zod Schemas Generation - Schema validation setup
- 🛡️ Shield Authorization - Type-safe permissions and rules
- 🧪 Examples - Working examples to explore
- ❓ FAQ / Troubleshooting - Common issues and solutions
- 🧑💻 Development - Contributing guidelines
- 🗺️ Roadmap - Planned features and improvements
- 📄 License - Licensing information
- 🙏 Acknowledgements - Credits and thanks
- 📝 Changelog - Version history and changes
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
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.0Click 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:
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.
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 supportedValidated 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"
}
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:
-
Automatic Integration: When
schemaLibrary = "zod"
is set, the generator automatically callsprisma-zod-generator
-
Configuration Management: Creates a
zod.config.json
file with optimized settings for oRPC usage -
Schema Output: Generates Zod schemas in the
zod-schemas/
subdirectory of your output path - Import Integration: Generated oRPC routers automatically import and use these schemas for validation
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"
}
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" |
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
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)
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.
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"
}
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.
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);
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.
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,
},
});
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 */]
});
Shield rules expect a Context
with user information:
interface Context {
prisma: PrismaClient;
user?: {
id: string;
email?: string;
name?: string;
roles?: string[];
permissions?: string[];
};
}
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
},
});
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 |
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
- Server: examples/basic/src/server.ts
- Seed: examples/basic/src/seed.ts
- Lib utilities: examples/basic/src/lib
- Example scripts: examples/basic/package.json
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
- Symptom: files not where you expect
- Action: verify your generator output path and config; compare with examples/basic/src/generated/orpc
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
- Symptom: documentation folder missing
- Action: set
generateDocumentation = "true"
and inspect src/generators/documentation-generator.ts
Shield path resolution errors
- Symptom: "Cannot find module" errors for shield imports
- Action: verify
shieldPath
points to correct file; check file exportspermissions
object; ensure path is relative to project root or absolute - Note: generator now handles dynamic path resolution automatically for common directory structures
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
andnpm run typecheck
pass before PR - Update README.md if flags/outputs change
- ✅ 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: MIT — see the
LICENSE
file. - Copyright © 2025 Omar Dulaimi.
- Prisma and its ecosystem
- oRPC community and patterns
- Zod for runtime validation
- TypeScript tooling
- Vitest and contributors
See CHANGELOG.md
Made with ❤️ by Omar Dulaimi