Support for 'ref' in zUUID, and 'refPath' in zId and zUUID
While implementing @zodyac/zod-mongoose in my project, I encountered limitations in the following use cases:
- Referencing another collection using
uuidinstead ofObjectId. - Array of references with
uuidorObjectId(depending on the model). - Using
refPathfor 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! π
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
.refPathforzUUIDandzObjectId
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
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 π
@tserdeiro Will do it asap to unblock your development, thanks for reaching out!
@tserdeiro Please check out version 3.2.0 and let me know if it solves your case
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:
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.