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

[Feature Request] Support for Nested Composite Types and Relations in MongoDB

Open drahmedshaheen opened this issue 10 months ago • 0 comments

I'm facing an issue with zod-prisma-types when using Prisma with MongoDB. In MongoDB, nested composite types are not relational; they are part of the same schema and should be treated as such. However, zod-prisma-types currently excludes these nested composite types, as if they were relational, which is not the case.

Additionally, even when composite types have nested composite types within them, zod-prisma-types does not generate Zod schemas correctly for these nested structures.

Could support be added to handle these nested composite types properly?

Example Prisma Schema

datasource db {
    provider = "mongodb"
    url      = env("DATABASE_URL")
}

generator zod {
    provider                  = "zod-prisma-types"
    createRelationValuesTypes = true
}

model User {
    id      String  @id @default(auto()) @map("_id") @db.ObjectId
    profile Profile
    posts   Post[]
}

type Profile {
    firstName String
    address   Address
}

type Address {
    street String
    city   String
}

model Post {
    id       String @id @default(auto()) @map("_id") @db.ObjectId
    author   User   @relation(fields: [authorId], references: [id])
    authorId String @db.ObjectId // relation scalar field  (used in the `@relation` attribute above)

    title String
}

Expected output

export const UserSchema = z.object({
  id: z.string(),
  profile: z.lazy(() => ProfileSchema),
})

export const ProfileSchema = z.object({
  firstName: z.string(),
  address: z.lazy(() => AddressSchema),
})

export const AddressSchema = z.object({
  street: z.string(),
  city: z.string(),
})

export const PostSchema = z.object({
  id: z.string(),
  authorId: z.string(),
  title: z.string(),
})

export const UserWithRelationsSchema: z.ZodType<UserWithRelations> = UserSchema.merge(
  z.object({
    posts: z.lazy(() => PostsSchema), // PostsSchema is schema for Modal not composite type
  }),
)

export const PostWithRelationsSchema: z.ZodType<PostWithRelations> = PostSchema.merge(
  z.object({
    author: z.lazy(() => UserSchema), // UserSchema is schema for Modal not composite type
  }),
)

Resulting output


export const UserSchema = z.object({
  id: z.string(),
})

export type User = z.infer<typeof UserSchema>

export type UserRelations = {
  profile: Profile
  posts: PostWithRelations[]
}

export type UserWithRelations = z.infer<typeof UserSchema> & UserRelations

export const UserWithRelationsSchema: z.ZodType<UserWithRelations> = UserSchema.merge(
  z.object({
    profile: z.lazy(() => ProfileSchema),
    posts: z.lazy(() => PostWithRelationsSchema).array(),
  }),
)

export const PostSchema = z.object({
  id: z.string(),
  authorId: z.string(),
  title: z.string(),
})

export type Post = z.infer<typeof PostSchema>

export type PostRelations = {
  author: UserWithRelations
}

export type PostWithRelations = z.infer<typeof PostSchema> & PostRelations

export const PostWithRelationsSchema: z.ZodType<PostWithRelations> = PostSchema.merge(
  z.object({
    author: z.lazy(() => UserWithRelationsSchema),
  }),
)

export const ProfileSchema = z.object({
  firstName: z.string(),
})

export type Profile = z.infer<typeof ProfileSchema>

export type ProfileRelations = {
  address: Address
}

export type ProfileWithRelations = z.infer<typeof ProfileSchema> & ProfileRelations

export const ProfileWithRelationsSchema: z.ZodType<ProfileWithRelations> = ProfileSchema.merge(
  z.object({
    address: z.lazy(() => AddressSchema),
  }),
)

export const AddressSchema = z.object({
  street: z.string(),
  city: z.string(),
})

export type Address = z.infer<typeof AddressSchema>

Package versions:

  • zod: 3.24.2
  • prisma: 6.4.0
  • zod-prisma-types: 3.2.4

drahmedshaheen avatar Feb 20 '25 12:02 drahmedshaheen