https://github.com/omar-dulaimi/prisma-orpc-generator
Prisma generator that creates fully-featured ORPC routers
https://github.com/omar-dulaimi/prisma-orpc-generator
orpc prisma prisma-orpc-generator prisma-zod-generator zod
Last synced: 26 days ago
JSON representation
Prisma generator that creates fully-featured ORPC routers
- Host: GitHub
- URL: https://github.com/omar-dulaimi/prisma-orpc-generator
- Owner: omar-dulaimi
- License: mit
- Created: 2025-09-03T01:06:06.000Z (about 1 month ago)
- Default Branch: master
- Last Pushed: 2025-09-03T01:51:08.000Z (about 1 month ago)
- Last Synced: 2025-09-03T03:19:59.699Z (about 1 month ago)
- Topics: orpc, prisma, prisma-orpc-generator, prisma-zod-generator, zod
- Language: TypeScript
- Homepage:
- Size: 188 KB
- Stars: 6
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
β‘ Prisma oRPC Generator
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.
```prisma
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"
}
``````bash
# generate
npx prisma generate
```---
## π Table of Contents
- [β‘ Quickstart](#quickstart) - Get up and running in minutes
- [ποΈ What Gets Generated](#what-gets-generated) - See what files are created
- [βοΈ Configuration](#configuration) - All available options
- [π§ Zod Schemas Generation](#zod-schemas-generation) - Schema validation setup
- [π‘οΈ Shield Authorization](#shield-authorization) - Type-safe permissions and rules
- [π§ͺ Examples](#examples) - Working examples to explore
- [β FAQ / Troubleshooting](#faq--troubleshooting) - Common issues and solutions
- [π§βπ» Development](#development-contributing) - Contributing guidelines
- [πΊοΈ Roadmap](#roadmap) - Planned features and improvements
- [π License](#license) - Licensing information
- [π Acknowledgements](#acknowledgements) - Credits and thanks
- [π Changelog](#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**
```bash
# 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)
```prisma
generator client {
provider = "prisma-client-js"
}generator orpc {
provider = "prisma-orpc-generator"
output = "./src/generated/orpc"
}
```
**Generate**
```bash
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.0---
## ποΈ What Gets GeneratedClick 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:
- Routers: [examples/basic/src/generated/orpc/routers](examples/basic/src/generated/orpc/routers)
- Zod schemas: [examples/basic/src/generated/orpc/zod-schemas](examples/basic/src/generated/orpc/zod-schemas)
- Tests: [examples/basic/src/generated/orpc/tests](examples/basic/src/generated/orpc/tests)
- Docs: [examples/basic/src/generated/orpc/documentation](examples/basic/src/generated/orpc/documentation)---
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](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](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](src/config/schema.ts).Example: focused configuration with Zod and docs
```prisma
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 GenerationClick to expand zod schemas info
This generator leverages [prisma-zod-generator](https://github.com/omar-dulaimi/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:
```json
{
"mode": "full",
"output": "./zod-schemas"
}
```Additional settings are only added when they differ from defaults:
```json
{
"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`:
```prisma
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 AuthorizationClick to expand shield authorization guide
The generator can automatically generate [orpc-shield](https://github.com/omar-dulaimi/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:
```prisma
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:**
```typescript
// In src/generated/orpc/routers/helpers/createRouter.ts
import { permissions } from '../../../../custom-shield';
export const or = os.$context().use(permissions);
```### Using Custom Shield Files
For advanced use cases, you can provide your own shield file instead of auto-generation:
```prisma
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:
```typescript
// 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()(({ ctx }) => !!ctx.user);
const isAdmin = rule()(({ ctx }) => ctx.user?.role === 'admin');
const isOwner = rule()(({ ctx, input }) => {
return ctx.user?.id === (input as any)?.userId;
});export const permissions = shield({
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:
```typescript
// Built-in rules (generated automatically)
const isAuthenticated = rule()(({ ctx }) => !!ctx.user);// Example custom rules (user-defined)
const isAdmin = rule()(({ 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({
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:
```typescript
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:
```typescript
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`:
```typescript
// Custom rule for post ownership
const isPostOwner = rule()(({ ctx, input }) => {
return ctx.user?.id === (input as any)?.authorId;
});// Use in shield config
const permissions = shield({
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 |---
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/schemasNotable files
- Server: [examples/basic/src/server.ts](examples/basic/src/server.ts)
- Seed: [examples/basic/src/seed.ts](examples/basic/src/seed.ts)
- Lib utilities: [examples/basic/src/lib](examples/basic/src/lib)
- Example scripts: [examples/basic/package.json](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](examples/basic/src/generated/orpc)Schema/config validation failures
- Symptom: errors referencing invalid options
- Action: check inputs against [src/config/schema.ts](src/config/schema.ts); fix paths/booleans; re-run generationDocs not emitted
- Symptom: documentation folder missing
- Action: set `generateDocumentation = "true"` and inspect [src/generators/documentation-generator.ts](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 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
```bash
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
```prisma
generator orpc {
provider = "../../lib/bin.js" // relative path to built generator
output = "./src/generated/orpc"
}
```Testing
```bash
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](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 contributorsSee [CHANGELOG.md](CHANGELOG.md)
---
Made with β€οΈ by [Omar Dulaimi](https://github.com/omar-dulaimi)