oRPC Generator
Generates oRPC routers per table, with optional validation reuse (Zod, Valibot, ArkType).
Options
interface GenerateOptions {
outputDir: string;
template?: 'standard' | 'minimal' | string;
includeRelations?: boolean;
naming?: { routerSuffix?: string; procedureCase?: 'camel' | 'kebab' | 'snake' };
format?: { enabled?: boolean; engine?: 'auto' | 'prettier' | 'biome'; configPath?: string };
templateOptions?: Record<string, unknown>;
validation?: {
useShared?: boolean;
library?: 'zod' | 'valibot' | 'arktype';
importPath?: string;
schemaSuffix?: string;
// How the validation generator named its exports. Normally inherited, see below.
affix?: {
tableCase?: 'preserve' | 'pascal';
schema?: { prefix?: Affix; suffix?: Affix };
type?: { prefix?: Affix; suffix?: Affix };
};
};
databaseInjection?: {
enabled?: boolean;
databaseType?: string;
databaseTypeImport?: { name: string; from: string };
};
servicesDir?: string;
}Database injection
When used with @drzl/template-orpc-service, the generator wires a dbMiddleware and passes context.db to your services. Configure the context type via databaseInjection.
validation: { library: 'valibot' },
databaseInjection: {
enabled: true,
databaseType: 'Database',
databaseTypeImport: { name: 'Database', from: 'src/db/db' },
},
servicesDir: 'src/services',In the generated router:
import type { Database } from 'src/db/db';
export const dbMiddleware = os.$context<{ db?: Database }>().middleware(/* ... */);Validation reuse
When validation.useShared is true, the generator imports Insert<Table>Schema, Update<Table>Schema, and Select<Table>Schema from your validation.importPath and wires them into handlers (inputs and outputs) based on the selected library.
Renamed schemas
If the validation generator uses an affix, the router has to import the renamed identifiers. You do not configure it twice. When exactly one other generator in the config produces the library named in validation.library, the CLI copies that generator's affix onto this one:
generators: [
{ kind: 'zod', path: 'src/validators/zod', affix: { tableCase: 'pascal' } },
{
kind: 'orpc',
validation: { useShared: true, library: 'zod', importPath: '../validators/zod' },
},
],// src/api/users.ts
import {
InsertUsersSchema as InsertusersSchema,
UpdateUsersSchema as UpdateusersSchema,
SelectUsersSchema as SelectusersSchema,
} from '../validators/zod';The local aliases stay Insert<tsName>Schema on purpose. They never leave the file, and keeping them fixed means an affix change never rewrites the router body.
Set validation.affix yourself only when the schemas come from somewhere DRZL does not generate. If you set it and it disagrees with the sibling generator, drzl generate fails with both sets of names in the error instead of emitting a router that cannot compile.
Output typing
The generator attaches .output(...) to each procedure. For example (Zod):
// list
os.output(z.array(SelectusersSchema)).handler(...)
// get
os.output(SelectusersSchema.nullable()).handler(...)
// create/update
os.output(SelectusersSchema).handler(...)
// delete
os.output(z.boolean()).handler(...)Valibot uses v.array(...) and v.nullable(...); ArkType uses SelectSchema.array() and SelectSchema.or('null').
Example
// drzl.config.ts
export default defineConfig({
schema: 'src/db/schemas/index.ts',
outDir: 'src/api',
generators: [
// `useShared` below imports these, and `template-orpc-service` imports the services, so
// both have to be generated. A config naming them without producing them emits routers
// that import modules nothing ever writes.
{ kind: 'zod', path: 'src/validators/zod', schemaSuffix: 'Schema' },
{ kind: 'service', path: 'src/services' },
{
kind: 'orpc',
template: '@drzl/template-orpc-service',
includeRelations: true,
naming: { routerSuffix: 'Router', procedureCase: 'kebab' },
validation: {
useShared: true,
library: 'zod',
importPath: 'src/validators/zod',
schemaSuffix: 'Schema',
},
},
],
});Run:
drzl generate -c drzl.config.tsRouters and an index barrel are generated at outDir.
Template hooks API
Templates expose a small API used by the generator.
interface ORPCTemplateHooks {
filePath(table, ctx): string;
routerName(table, ctx): string;
procedures(table): Array<{ name: string; varName: string; code: string }>;
imports?(tables, ctx): string;
prelude?(tables, ctx): string;
header?(table): string;
}filePath: absolute output path for a table’s routerrouterName: exported const name of the routerprocedures: code snippets for each handler variable (varName) and exported key (name)imports: extra imports at file topprelude: code emitted after imports (utility helpers, etc.)header: a banner/comment string at the top of the file
See also: oRPC + Service Template and Standard Template
Generated Output License
- You own the generated output. DRZL grants you a worldwide, royalty‑free, irrevocable license to use, copy, modify, and distribute the generated files under your project’s license.
- A short header is added by default. Configure via
outputHeaderindrzl.config.ts:outputHeader.enabled = falseto disableoutputHeader.text = '...'to customize
Need something else?
If this generator doesn't cover what you need, DM me on X (https://x.com/omardulaimidev) and we can scope it together.

