Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for 'ref' in zUUID, and 'refPath' in zId and zUUID #27

Open
tserdeiro opened this issue Jan 10, 2025 · 5 comments
Open

Support for 'ref' in zUUID, and 'refPath' in zId and zUUID #27

tserdeiro opened this issue Jan 10, 2025 · 5 comments
Labels
enhancement New feature or request

Comments

@tserdeiro
Copy link

tserdeiro commented Jan 10, 2025

While implementing @zodyac/zod-mongoose in my project, I encountered limitations in the following use cases:

  1. Referencing another collection using uuid instead of ObjectId.
  2. Array of references with uuid or ObjectId (depending on the model).
  3. Using refPath for dynamic population based on another document property (Mongoose documentation).

Currently, zUUID does not support the ref option, and neither zId nor zUUID accept refPath. This makes it difficult to work with dynamic references and uuid identifiers.

Would it be possible to add support for these features?


Expected Usage Example

import { Schema } from 'mongoose';
import { v4 as uuidv4 } from 'uuid';

const accountSchema = new Schema({
  _id: {
    default: uuidv4, // Use uuidv4 instead of ObjectId
    type: String,
  },
  name: {
    trim: true,
    type: String,
  },
  referrer: {
    ref: 'Account', // Reference to another account by uuid
    type: String,
  },
});

const anotherSchema = new Schema({
  _id: {
    default: uuidv4, // Use uuidv4 instead of ObjectId
    type: String,
  },
  account: {
    ref: 'Account', // Reference to an account uuidv4
    required: true,
    type: String,
  },
  docModel: {
    enum: Object.values(Models),
    required: true,
    type: String,
  },
  ref: {
    refPath: 'docModel', // Dynamic refPath usage
    required: false,
    type: Schema.Types.Mixed,
  },
  refs: {
    refPath: 'docModel', // Array of references (ObjectIds or uuidv4 depending on the model)
    required: false,
    type: Schema.Types.Mixed,
  },
});

Thank you for your attention and for the excellent work on this library. Looking forward to your response! 🖖

@bebrasmell bebrasmell added the enhancement New feature or request label Jan 12, 2025
@bebrasmell
Copy link
Collaborator

bebrasmell commented Jan 12, 2025

Oi! Sounds good, but keep in mind that UUIDs are poorly supported by mongoose itself.
I could do two little quick-win improvements:

  • Add a ref option for zUUID
  • Add .refPath for zUUID and zObjectId

There are several edge-cases though:
It wont add any kind of casting / validation to UUIDs and you will have to deal with those using third-party libraries and probably extend your schemas manually anyway

Ah, and different MongoDB ORMs are using different UUID versions, so please keep that in mind if you are looking into interoperating with other languages / services

@tserdeiro
Copy link
Author

Hi @bebrasmell, thank you for your quick response! 😊

The two improvements you mentioned sound great and would definitely solve the current limitation.
Do you have an estimate for when they might be implemented?
We started integrating @zodyac/zod-mongoose, but we had to pause midway due to this issue 😅

Regarding the UUID observation, I reviewed the StackOverflow references you mentioned, and I believe they might be a bit outdated.
Recent versions of Mongoose (specifically version 8, which @zodyac/zod-mongoose is based on) seem to handle UUIDs without any issues (official documentation).

As for validations, you’re right that third-party libraries may be necessary in some cases. Fortunately, zod already includes a built-in validator for uuid, which simplifies this process:

z.string().uuid().unique().default(uuidv4);

Additionally, the example schemas I shared are based on real-world use cases we have in production with mongoose@7, where we use dynamic references (refPath) and arrays of uuid without any problems. Everything works smoothly!

Thank you again for your support!
Looking forward to your response 🖖

@bebrasmell
Copy link
Collaborator

@tserdeiro Will do it asap to unblock your development, thanks for reaching out!

@bebrasmell
Copy link
Collaborator

@tserdeiro Please check out version 3.2.0 and let me know if it solves your case

@tserdeiro
Copy link
Author

Hi @bebrasmell, thank you for the update! 😊

I've updated to version 3.2.0, and I can now use zUUID(ref?: string) and .refPath! 🎉


However, I'm encountering the following error:

Error Image

index.js:656 Uncaught TypeError: Cannot read properties of undefined (reading 'UUID')
    at parseUUID (index.js:480:61)
    at parseField (index.js:304:12)
    at parseObject (index.js:286:17)
    at zodSchema (index.js:274:22)
    at eval (model.ts:49:25)
    at ./src/backend/modules/audit-logs/model.ts (accounts.js:895:1)
    at options.factory (webpack.js:706:31)
    at __webpack_require__ (webpack.js:37:33)
    at fn (webpack.js:362:21)
    at eval (mongoose-audit-log-plugin.ts:17:91)
    at ./@core/libs/mongoose-audit-log-plugin.ts (accounts.js:818:1)
    at options.factory (webpack.js:706:31)
    at __webpack_require__ (webpack.js:37:33)
    at fn (webpack.js:362:21)
    at eval (model.ts:8:94)

Here’s the zod schema I'm using:

export const auditLogSchema = z.object({

  _id: z.string().uuid()
    .unique()
    .default(uuidv4),

  account: zUUID(Subject.ACCOUNT),

  changes: z.any(),

  issuer: zUUID(Subject.ACCOUNT),

  ref: zId().refPath('subject')
    .or(
      zUUID().refPath('subject')
    ),

  refs: z.array(
    zId().refPath('subject')
      .or(
        zUUID(Subject.ACCOUNT).refPath('subject')
      )
  ),

  subject: z.enum(Object.values(Subject) as [Subject, ...Subject[]])

})

const schema = zodSchema(auditLogSchema, { // error here
  collection: 'audit_logs',
  timestamps: true,
  versionKey: false
})

And the generated Mongoose schema:

{
  "_id": {
    "default": "ffa04949-9992-4c17-92fb-2fcaf5c6ba11",
    "required": true,
    "unique": true
  },
  "account": {
    "required": true,
    "unique": false,
    "ref": "Account"
  },
  "changes": {
    "required": true
  },
  "issuer": {
    "required": true,
    "unique": false,
    "ref": "Account"
  },
  "ref": {
    "required": true,
    "unique": false,
    "refPath": "subject"
  },
  "refs": {
    "type": [
      {
        "required": true,
        "unique": false,
        "refPath": "subject"
      }
    ],
    "required": true
  },
  "subject": {
    "unique": false,
    "enum": [
      "Account",
      "... other subjects ..."
    ],
    "required": true
  }
}

I also have a quick question:
Should changes: z.any() result in:

"changes": {
  "required": true,
  "type": "Schema.Types.Mixed"
}

Instead of:

"changes": {
  "required": true
}

If so, it seems that the schema is not being generated correctly.

Thank you again for your support and the improvements! 😊
Looking forward to your feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants