From Prisma to Zod: Streamlined Types, Validation, and Error Handling
In most modern TypeScript applications, data moves across multiple layers: database, business logic, API endpoints, frontend forms. But here's the catch – most teams define the same data structure multiple times, in multiple places, with very slight changes. This creates redundancy, inconsistency, and bugs that sometimes only show up at runtime.
But what if we could define our data once, and then automatically generate:
- type-safe validation schemas,
- precise runtime validators,
- concise error handling,
- schemas for documentation.
In this blog post, we’ll explore how to go from Prisma schemas, all the way up to API contracts and docs – by using your database schema as the single source of truth for everything.
One Source of Truth: The Prisma Schema
For our database connection and data modeling, we will be using Prisma. Using Prisma schemas, we can define the core of our data model – what entities will exist, how they’ll relate, and what rules they’ll follow. We’ll use a simple example:
model User {
id String @id @default(uuid())
email String @unique
name String?
createdAt DateTime @default(now())
}This is now your source of truth for the User entities. But the constraints we set here can only be enforced at the database level – your API validation, form validation, and documentation live in other files.
And this is where Prisma plugins come in.
Prisma Plugins: Code Generation Power
Prisma supports a powerful plugin system through the generator block in your schema.prisma file. If you’ve worked with Prisma before, you have already worked with generators. If you take a look at the schema file, you’ll notice that the client you use to create queries is also generated based on the schema.
generator client {
provider = "prisma-client-js"
}But the real magic comes from custom generators – tools that let you create TypeScript code automatically based on your Prisma models. And one such tool is zod-prisma-types.
Enter zod-prisma-types
By adding this generator to your Prisma setup, you can generate Zod schemas for every Prisma model automatically. Zod is a Typescript-first validation tool that also uses schemas to define rules it will apply to data.
Setup
Add to your schema.prisma:
generator zod {
provider = "zod-prisma-types"
}Then run:
npx prisma generateAnd boom! You now have a folder full of Zod schemas like:
export const UserModel = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().nullable().optional(),
createdAt: z.date(),
});These are fully type-safe, inline with your database schema, and automatically updated whenever your Prisma models change.
Runtime Validation with Zod
With Zod, you can now validate inputs at runtime using the schemas generated from your models. You can wrap the validation in middleware, or just use it on the spot like this:
import { UserModel } from "./zod/user";
const userData = req.body;
const parsed = UserModel.parse(userData);For more complex fields with additional validation needed, such as the email field in our example, we can add comments to our schema, like this:
model User {
id String @id @default(uuid())
///@zod.custom.use(z.string().email().min(5).max(100).openapi({description: "Email.", type: "string"}))
email String @unique
name String?
createdAt DateTime @default(now())
}That will effectively replace the default validation with the specified custom conditions instead.
And for very special cases, you can even extend the generated schemas using Zod’s .extend() or .superRefine() methods.
Zod + Swagger
Now that we’ve specified the validation schemas, all that is left is to use them in the documentation. And we can do that using libraries such as zod-openapi, that can convert your Zod schemas to OpenAPI definitions, which power Swagger UI.
This means:
- Your Prisma model defines the data.
- Zod schema validates it at runtime.
- OpenAPI docs are generated from the same Zod schema.
Yes – your database schema and all that you’ve specified there, now becomes your API schema, both in runtime behavior and documentation.
Wrap-Up
The best code is the code you don’t have to write – or even worse, maintain in four different places.
By using the most out of your Prisma schema and turning it into Zod schemas and then OpenAPI docs, you unlock a streamlined, scalable, and type-safe development workflow that’s easier to debug and easier to love.
From database to documentation, all from one schema file.