Skip to content

oRPC Generator

Generates oRPC routers per table, with optional validation reuse (Zod, Valibot, ArkType).

Options

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

ts
validation: { library: 'valibot' },
databaseInjection: {
  enabled: true,
  databaseType: 'Database',
  databaseTypeImport: { name: 'Database', from: 'src/db/db' },
},
servicesDir: 'src/services',

In the generated router:

ts
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:

ts
generators: [
  { kind: 'zod', path: 'src/validators/zod', affix: { tableCase: 'pascal' } },
  {
    kind: 'orpc',
    validation: { useShared: true, library: 'zod', importPath: '../validators/zod' },
  },
],
ts
// 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):

ts
// 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

ts
// 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:

bash
drzl generate -c drzl.config.ts

Routers and an index barrel are generated at outDir.

Template hooks API

Templates expose a small API used by the generator.

ts
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 router
  • routerName: exported const name of the router
  • procedures: code snippets for each handler variable (varName) and exported key (name)
  • imports: extra imports at file top
  • prelude: 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 outputHeader in drzl.config.ts:
    • outputHeader.enabled = false to disable
    • outputHeader.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.

Need a custom template or integration? DM @omardulaimidev on X.