DRZL is a code generator for Drizzle ORM. Point it at the schema you already wrote and it emits the validation schemas, API routers and typed services to match. Open any row below to read the file it writes.
src/schema.ts
export const users =
pgTable('users', {
id: integer(),
email: varchar(),
age: integer(),
tier: text(),
});src/gen/users.zod.ts
import { z } from 'zod';
export const usersInsertSchema = z.object({
id: z.number().int(),
email: z.string().max(255),
age: z.number().int().gte(18),
tier: z.string(),
});
// plus usersSelectSchema, same shapesrc/gen/users.valibot.ts
import * as v from 'valibot';
export const usersInsertSchema = v.object({
id: v.pipe(v.number(), v.integer()),
email: v.pipe(v.string(), v.maxLength(255)),
age: v.pipe(v.number(), v.minValue(18)),
});src/gen/users.schema.json
{
"type": "object",
"required": ["email", "age"],
"properties": {
"email": { "type": "string", "maxLength": 255 },
"age": { "type": "integer", "minimum": 18 }
}
}src/api/users.router.ts
import { usersInsertSchema } from '../gen';
export const usersRouter = router({
create: publicProcedure
.input(usersInsertSchema)
.mutation(({ input }) => UsersService.create(input)),
});src/api/users.routes.ts
app.post('/users',
sValidator('json', usersInsertSchema),
async (c) => c.json(
await UsersService.create(c.req.valid('json')), 201));src/api/dto/create-user.dto.ts
export class CreateUserDto {
@IsString() @MaxLength(255) email: string;
@IsInt() @Min(18) age: number;
}src/api/schema.graphql
type User {
id: Int!
email: String!
age: Int!
}
input CreateUserInput { email: String! age: Int! }src/services/users.service.ts
export class UsersService {
static async create(input: UsersInsert) {
return db.insert(users).values(input).returning();
}
}Everything here is a generator DRZL ships, a provider with a quickstart, or a runtime the output is measured on. Each name links to the page that backs it.
Six generator kinds, plus the OpenAPI document the JSON Schema generator writes.
One generator kind each, emitting the router or the app that framework expects, except react-hook-form and TanStack Form, which are the two targets of the one forms kind.
The dialects shown here. The analyzer reads seven; the first three below are asked a real server on every commit, and the providers under them have quickstarts.
The emitted code and the CLI were run on all three, and the generated tree is byte-identical across them.
React Hook Form and TanStack Form are the two targets of one forms kind rather than a generator each. Every schema DRZL emits in zod, valibot or arktype spelling carries Standard Schema v1, which both accept directly, so the resolver is one line either way; what the generator adds is the per-field metadata a control needs, which is the half you would otherwise write by hand.
Product names and marks belong to their owners, and appear here only to say which software DRZL generates code for. No endorsement or affiliation is implied by any of them.
DRZL also reads the CHECK constraints declared on your table, which most generators skip, so a row that passes the generated schema is a row the database will accept. On the eight-column table the benchmark uses that is four of the four constraints, against none. Each one the first-party schema misses is a row that passes validation and then fails at the database, which is the worst place to find out. Rejecting a bad row costs about the same either way, within a few percent, and that is the path an API actually spends its validation time in. Accepting a good row costs DRZL 15% to 21% more, depending on the run, and that is the price of the four extra checks.
The throughput figures, the generated file size and the machine they were measured on stay on one page rather than being repeated here: a second copy of a measurement is a copy that goes stale quietly. The constraint count is not a measurement of a machine, and the same question is put to a real Postgres, a real SQLite and a real MySQL on every commit, which is how it is verified.