A custom prisma generator that creates Zod schemas from your Prisma model.
because the zod-prisma package is without updates for a while, I made a fork to advance my pull request
Explore the docs »
View Demo · Report Bug · Request Feature
I got tired of having to manually create Zod schemas for my Prisma models and of updating them everytime I made schema changes. This provides a way of automatically generating them with your prisma
To get a local copy up and running follow these simple steps.
This project utilizes yarn and if you plan on contributing, you should too.
npm install -g yarn
-
Ensure your tsconfig.json enables the compiler's strict mode. Zod requires it and so do we, you will experience TS errors without strict mode enabled
-
Add zod-prisma as a dev dependency
yarn add -D @pyxlab/zod-prisma
-
Add the zod-prisma generator to your schema.prisma
generator zod { provider = "@pyxlab/zod-prisma" output = "./zod" // (default) the directory where generated zod schemas will be saved relationModel = true // (default) Create and export both plain and related models. // relationModel = "default" // Do not export model without relations. // relationModel = false // Do not generate related model modelCase = "PascalCase" // (default) Output models using pascal case (ex. UserModel, PostModel) // modelCase = "camelCase" // Output models using camel case (ex. userModel, postModel) // modelCase = "snake_case" // Output models using snake case (ex. user_model, post_model) modelSuffix = "Model" // (default) Suffix to apply to your prisma models when naming Zod schemas // useDecimalJs = false // (default) represent the prisma Decimal type using as a JS number useDecimalJs = true // represent the prisma Decimal type using Decimal.js (as Prisma does) imports = null // (default) will import the referenced file in generated schemas to be used via imports.someExportedVariable includeTypename = true // (default) includes the __typename field in the generated schema // includeTypename = false // does not include the __typename field in the generated schema // includeTypename = "model" // changes the typename to the one passed in quotes __typename -> __model typenameCase = "PascalCase" // (default) Output __typename using pascal case (ex. User, Post, UserPost) // typenameCase = "camelCase" // Output __typename using camel case (ex. user, post, userPost) // typenameCase = "snake_case" // Output __typename using snake case (ex. user, post, user_post) // https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-by-null-values prismaJsonNullability = true // (default) uses prisma's scheme for JSON field nullability // prismaJsonNullability = false // allows null assignment to optional JSON fields }
-
Run
npx prisma generate
oryarn prisma generate
to generate your zod schemas -
Import the generated schemas form your selected output location
Rich-comments in the Prisma schema will be transformed into JSDoc for the associated fields:
Note: make sure to use a triple-slash. Double-slash comments won't be processed.
model Post {
/// The unique identifier for the post
/// @default {Generated by database}
id String @id @default(uuid())
/// A brief title that describes the contents of the post
title String
/// The actual contents of the post.
contents String
}
Generated code:
export const PostModel = z.object({
/**
* The unique identifier for the post
* @default {Generated by database}
*/
id: z.string().uuid(),
/**
* A brief title that describes the contents of the post
*/
title: z.string(),
/**
* The actual contents of the post.
*/
contents: z.string(),
})
You can also use the @zod
keyword in rich-comments in the Prisma schema
to extend your Zod schema fields:
model Post {
id String @id @default(uuid()) /// @zod.uuid()
/// @zod.max(255, { message: "The title must be shorter than 256 characters" })
title String
contents String /// @zod.max(10240)
}
Generated code:
export const PostModel = z.object({
id: z.string().uuid(),
title: z.string().max(255, { message: 'The title must be shorter than 256 characters' }),
contents: z.string().max(10240),
})
Sometimes its useful to define a custom Zod preprocessor or transformer for your data. zod-prisma enables you to reuse these by importing them via a config options. For example:
generator zod {
provider = "zod-prisma"
output = "./zod"
imports = "../src/zod-schemas"
}
model User {
username String /// @zod.refine(imports.isValidUsername)
}
The referenced file can then be used by simply referring to exported members via imports.whateverExport
.
The generated zod schema files will now include a namespaced import like the following.
import * as imports from '../../src/zod-schemas'
In conjunction with this import option, you may want to utilize an entirely custom zod schema for a field.
This can be accomplished by using the special comment directive @zod.custom()
.
By specifying the custom schema within the parentheses you can replace the autogenerated type that would normally be assigned to the field.
For instance if you wanted to use
z.preprocess
Zod schemas can be used to force data into a particular shape. This is useful, for example, if you want to string into a number or date. To do this, you can use the @zod.coerce()
directive.
model User {
id String @id @default(uuid())
name String /// @zod.coerce
age Int /// @zod.coerce.min(18)
}
Generated code:
export const UserModel = z.object({
id: z.string().uuid(),
name: z.coerce.string(),
age: z.coerce.number().min(18),
})
Zod messages can be used to override the default error message for a field. To do this, you can use the @zod.message()
directive.
If the table field is required, the message defaults to required_error, and if null, it defaults to invalid_type_error, so you can enter either or both.
model User {
id String @id @default(uuid())
name String /// @zod.message('Name is required')
phone String? /// @zod.message('Phone must be a string')
age Int /// @zod.message({ required_error: 'Age is required', invalid_type_error: 'Age must be at least 18' })
}
Generated code:
export const UserModel = z.object({
id: z.string().uuid(),
name: z.string({ required_error: 'Name is required' }),
phone: z.string({ invalid_type_error: 'Phone must be a string' }).nulish(),
age: z
.number({
required_error: 'Age is required',
invalid_type_error: 'Age must be at least 18',
})
.min(18),
})
JSON fields in Prisma disallow null values. This is to disambiguate between setting a field's value to NULL in the database and having a value of null stored in the JSON. In accordance with this zod-prisma will default to disallowing null values, even if your JSON field is optional.
If you would like to revert this behavior and allow null assignment to JSON fields,
you can set prismaJsonNullability
to false
in the generator options.
For examples, please refer to the Examples Directory or the Functional Tests
See the open issues for a list of proposed features (and known issues).
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Distributed under the MIT License. See LICENSE
for more information.
Carter Grimmeisen - [email protected]
Project Link: https://github.com/Pyxlab/zod-prisma