chrishoermann / zod-prisma-types

Generator creates zod types for your prisma models with advanced validation
Other
579 stars 43 forks source link

It's not possible to omit relation field #110

Open oceandrama opened 1 year ago

oceandrama commented 1 year ago

With this schema:

model Article {
  id        Int   @id @default(autoincrement())
  authorId  Int?
  author    User? @relation(fields: [authorId], references: [id])
}

it generates

export const ArticleCreateInputSchema: z.ZodType<Prisma.ArticleCreateInput> = z
  .object({
    author: z.lazy(() => UserCreateNestedOneWithoutArticlesInputSchema).optional()
  })
  .strict();

export const ArticleUncheckedCreateInputSchema: z.ZodType<Omit<Prisma.ArticleUncheckedCreateInput, "userId">> = z
  .object({
    id: z.number().int().optional(),
    authorId: z.number().int().optional().nullable()
  })
  .strict();

You can add omit to authorId

model Article {
  id        Int   @id @default(autoincrement())
  authorId  Int? /// @zod.custom.omit([input])
  author    User? @relation(fields: [authorId], references: [id])
}

to get this

export const ArticleCreateInputSchema: z.ZodType<Prisma.ArticleCreateInput> = z
  .object({
    author: z.lazy(() => UserCreateNestedOneWithoutArticlesInputSchema).optional()
  })
  .strict();

export const ArticleUncheckedCreateInputSchema: z.ZodType<Omit<Prisma.ArticleUncheckedCreateInput, "userId">> = z
  .object({
    id: z.number().int().optional(),
    // omitted: authorId: z.number().int().optional().nullable() })
  .strict();

It's still possible to create article with author using author: { connect: { id } } instead of authorId

And if you try to add omit to author too

model Article {
  id        Int   @id @default(autoincrement())
  authorId  Int? /// @zod.custom.omit([input])
  author    User? @relation(fields: [authorId], references: [id]) /// @zod.custom.omit([input])
}

you recieve an error:

[@zod generator error]: Validator 'custom' is not valid for type 'User'. [Error Location]: Model: 'Article', Field: 'author'.
chrishoermann commented 1 year ago

@oceandrama thanks for the hint. I need to look into this in more detail but I think it is because the omitted fields are currently only propagated to the "normal" create, update, etc. input types and not to the nested ones.

andenacitelli commented 1 year ago

I think it is because the omitted fields are currently only propagated to the "normal" create, update, etc. input types and not to the nested ones.

Glad I found this issue, was messing up my use case. Will leave some info incase it helps.

Relevant model from my schema:

model Task {
  id          String    @id @default(auto()) @map("_id") @db.ObjectId
  completed   Boolean   @default(false)
  title       String /// @zod.string.min(1)
  size        TaskSize  @default(MEDIUM)
  description String?
  number      Int   /// @zod.custom.omit(["model", "input"])
  start       DateTime? // Date at which this task can be worked on
  end         DateTime? // Day this task is due
  url         String? // Page that triggered this task to be created
User             User            @relation(fields: [userEmail], references: [email])
  userEmail        String          @unique
}

I am validating my input using the generated TaskUncheckedCreateWithoutUserInputSchema, but was confused why the annotation on the number field wasn't seeming to propogate to the generated schema. I generate that value serverside and append it to the user input, so wanted to omit it from the input types.

I suppose my solution will be to use the base unchecked input type, and simply omit both the user and number types from the input. Would be nice to get support added for the omits on the non-base input versions at some point in the future, or at least update the README to make that clearer.

EDIT: Yep, worked like a charm. As per the OP though, I think you have to stick to the unchecked schema variants for it to work properly.

samthompsonkennedy commented 1 year ago

Just thought I'd bump this as well, running into a simlar issue. My use case is that when a user applies for a job, we want to auto-fill the applicant from the auth context.

model JobApplication {
  id           String @id @default(cuid())
  cover_letter String

  job_post    JobPost @relation(fields: [job_post_id], references: [id], onDelete: Cascade)
  job_post_id String

  applicant    Profile @relation(fields: [applicant_id], references: [id], onDelete: Cascade)
  applicant_id String /// @zod.custom.omit([input])

  @@index([job_post_id])
  @@index([applicant_id])
}

Using the unchcked inputs isn't too practical in my example

const application = await ctx.prisma.jobApplication.create({
    data: {
        ...input,
        job_post_id: undefined,
        applicant: {
            connect: {
                id: ctx.session.user.profile_id,
            },
        },
        job_post: {
            connect: {
                id: input.job_post_id,
            },
        },
    },
});

Ideally would love to be able to go

...
applicant    Profile @relation(fields: [applicant_id], references: [id], onDelete: Cascade) /// @zod.custom.omit([input])
...

const application = await ctx.prisma.jobApplication.create({
    data: {
        ...input,
        applicant: {
            connect: {
                id: ctx.session.user.profile_id,
            },
        },
    },
});```