zod-prisma-types icon indicating copy to clipboard operation
zod-prisma-types copied to clipboard

[BUG] Incompatibility with prisma-json-types-generator

Open RicSala opened this issue 1 year ago • 3 comments

Describe the bug

(Not sure if it's a bug or a feature, but here it goes...) When trying to combine both libraries, zod-prisma-types does not take into account the typed json fields, thus generating more general type (json) that cannot be assign to the narrower file generated by prisma with the prisma-json-types-generator

Package versions (please complete the following information): "zod": "^3.23.8" "prisma": "^5.17.0", "prisma-json-types-generator": "^3.1.1",

and then I use "npx zod-prisma-types" in the provider configuration.

Additional context It's the recommended generator for json files from prisma: https://www.prisma.io/docs/orm/prisma-client/special-fields-and-types/working-with-json-fields#using-prisma-json-types-generator

If there is a way to achieve the same result only using zod-prisma-types, I would love to know how.

Thanks, the library is pretty useful!

RicSala avatar Oct 17 '24 19:10 RicSala

Hello @RicSala, I have successfully circumvented this problem using custom imports:

model FormType {
  /// [FormTypeFeatures]
  features  Json /// @zod.import(["import { formTypeFeaturesZod } from '../../../validation/formTypeFeatures'"]).custom.use(formTypeFeaturesZod)
}

And in the PrismaJson namespace:

declare global {
  namespace PrismaJson {
    type FormTypeFeatures = PrismaValidation.FormTypeFeatures
  }
}

Where PrismaValidation.FormTypeFeatures is z.infer<typeof formTypeFeaturesZod>. The validation directory contains the custom zod validator for the JSON field.

And to safe check persisting the data I also use:

const prismaClientSingleton = () => {
  return new PrismaClient({
    log: ["info", "error"],
  }).$extends({
    query: {
      formType: {
        create({ args, query }) {
          args.data.features = PrismaValidation.formTypeFeaturesZod.parse(
            args.data.features,
          )
          return query(args)
        },
      },
    },
  })
}

However, I am not sure if the create extension is executed on nested inserts. I think I read something like that in the Prisma docs.

aqos156 avatar Oct 29 '24 10:10 aqos156

@aqos156 I did exactly that too! But in my case that's not doing the trick, as I get the following generated schema (example with UserCreateInputSchema ):

export const UserCreateInputSchema: z.ZodType<Prisma.UserCreateInput> = z.object({
  ...
  customImage: z.union([ z.lazy(() => JsonNullValueInputSchema),filesUploadedSchema ]),
  ...
  }).strict();

The problem is that I am getting that union with JsonNullValueInputSchema, when it should only be "filesUploadedSchema (I mean, the "JsonNull" is unexpected and causin the issue).

The type created by the library "prisma-json-types-generator" is generating the correct type for "customImage", which does not include a "null".

As the generic in the type notation (Prisma.UserCreateInput) does not fit the inferred type of the schema (because of the null option) it's throwing an error.

Are you doing something different? Are you getting that "jsonNull" too? If so, how come it's not problematic for you?

Cheers!

RicSala avatar Nov 02 '24 20:11 RicSala

I also ran into this problem after upgrading prisma-json-types-generator from v3.1.1 to v3.2.x today (Prisma v5.2.2), and spent about a day to look into the solutions. Although I didn't find a good way to resolve it, I would like to share some findings and workarounds for others who are struggling with it.

First of all, it seems that the root cause is zod-prisma-types finds/parses multiple types on the JSON field from the Prisma DMMF , so it creates a union, and the JsonNullValueInputSchema comes from the first type which is a NonScalarType, the second type is a special JSON type and got overwritten by the custom validators. The DMMF is not affected by prisma-json-types-generator, so zod-prisma-types will use the data from the original models.

I tried the workaround from @aqos156, and got the same issue as @RicSala. For me one of the caveats is that I need to put zod.import() on the Model level, the field level import doesn't generate the import on the output of zod-prisma-types. These are the things I have thought about/tried:

  1. Disable the generation of the JsonNullValueInputSchema type in zod-prisma-types: I couldn't find a way to do so based on some rough readings of the source code, zod-prisma-types doesn't seem to support this at the moment
  2. Disable the Prisma JSON null value: This will remove the null value from the DMMF and thus resolve the issue, but unfortunately Prisma doesn't seem to support this
  3. Add the missing null types to prisma-json-types-generator so that it matches the generated types from zod-prisma-types : This can be done either on prisma-json-types-generator declaration or on your own Zod type, but it does add the unwanted JSON null values to your type, and might break your existing use cases
    1. Option 1: Add the types to the Prisma schema on prisma-json-types-generator. This will keep your original type (Custom Type) as is, and you can use it in other places without the added null type (stricter)
      // Prisma.schema
      /// [CustomType | "JsonNull" | NullableJsonNullValueInput]
      jsonField Json /// @zod.custom.use(CustomTypeZodSchema)
      
      // TypeDef.ts
      type CustomType = z.infer<typeof CustomTypeZodSchema>;
      
    2. Option 2: Add the types to the custom type Zod schema
      // TypeDef.ts
      import { JsonNullValueInputSchema } from "prisma/generated/zod";
      CustomTypeZodSchema = z
        .object({
          ...
        })
        .or(z.enum(["JsonNull"]))
        .or(JsonNullValueInputSchema);
      
  4. Patch the generated code (sed/git patch/etc.) after prisma generate. I didn't go down this route (explained below) but it should give you the most ideal result, although it is a bit more hacky.

At the end, I looked back at my use cases, and realized that I was only using the model types generated from zod-prisma-types, i.e. input types were not in used, so I simply disabled the input types generation and the issue went away.

I hope this could help whoever comes to this post in the future. BTW, the generated input types seems to have other issues on Prisma 6.x.

CHC383 avatar Dec 03 '24 04:12 CHC383