New Package pmogollons:zod-schema

pmogollons:zod-schema integrates Zod with Meteor Collections, allowing you to define and attach zod schemas that will validate insert, update and upsert ops. It also adds withDates, withUser and withSoftDelete to handle timestamps, userId on insert and soft delete for docs that can be recovered.

Key Benefits:

  • Seamless Zod Integration: Use Zod’s powerful schema definitions to validate mongo ops in your Meteor app.
  • Validation and Parsing: Automatically parse and validate mongo ops, ensuring data integrity across your application.
  • Error Handling: Uses and extension of ValidatedError that are easily rendered in the UI.
  • Server only: It’s imported only on the server, so mongo ops validation only happens on server environment.
  • Extensions: You can extend the functionality and schema using withUser, withDates and withSoftDelete.
  • Easy to Implement: Simple integration into your existing Meteor codebase without needing to overhaul your current structure.
  • Flexible Validation: Skip schema validation when necessary, providing flexibility for specific use cases.

Example

import { z } from "zod";
import { Mongo } from "meteor/mongo";

const contactsSchema = z.object({
  name: z.string(),
  age: z.number(),
});

const Contacts = new Mongo.Collection("contacts");

Contacts.withSchema(contactsSchema);

// Will add isDeleted: false and removing docs will result in setting isDeleted: true
UserCollection.withSoftDelete(); 
// Will add createdAt and updatedAt fields
UserCollection.withDates(); 
// Will add userId field on insert
UserCollection.withUser(); 

await Contacts.insertAsync({ name: "Alice", age: 30 }); // Valid
await Contacts.insertAsync({ name: "Bob", age: "thirty" }); // Throws ValidationError
await Contacts.insertAsync({ name: "Bob", miaw: "miaw" }); // Will remove the "miaw" field

// Will skip schema validation and allow insert of any doc
await UserCollection.insertAsync({ name: "David", age: "35" }, { skipSchema: true });

Why not use aldeed:collection2

While aldeed:aldeed:collection2 is a great tool, pmogollons:zod-schema offers a more modern and simple approach to schema validation, leveraging the power of Zod. It’s perfect for those who prefer a more functional programming style, with full support for TypeScript.

Check the GitHub repository if you want to know more.


If you have any ideas or feedback, feel free to share them below or reach out on Discord @paulomogollon.

PD: The packages that I have being publishing are the prelude for the publishing of pmogollons:nova, a package that will strive to replace grapher using @bluelibs/nova.

19 Likes

Looks really cool! Very idiomatic, I would say
Great job @pmogollon :clap:

Thanks, Im trying to give back a little bit to the meteor community.

6 Likes

I’m absolutely using this, thank you very much

Awesome, let me know how it goes.

withUser and withDates is super useful. Kind of reminds me of jagi:astronomy which I think used to have something similar. I think we are nearing the point of combining all of these into something comprehensive that can be added to the core.

4 Likes

Version 1.0.3 released

  • Added support for $eachwith $position, $slice and $sort operators.
  • Fixed issue with array $push and $addToSet
  • Added missing withUser type
  • Added more tests

Hi, your package seem’s awesome !
We’ve tried to use aldded:collection2 with zod but no success (too much effort for a little new app). So we’ve been waiting for such a package like yours to came out.
(right now we are just be super careful on our Mongo call to avoid inserting a bad document, with the help of zodern:relay)

Some little suggestions :

  1. In our app, meta fields like createdAt are renamed meta_create.at. It would be cool to override your default field added with withDates and to other with functions.
// Our common base schema
export const collectionBaseSchema = z.strictObject({
  _id: z.string(),
  meta_created: z.strictObject({
    by: z.string(), // User._id
    at: z.date(),
  }),
  meta_updated: z.optional(
    z.strictObject({
      by: z.string(), // User._id
      at: z.date(),
    })
  ),
  meta_disabled: z.optional(
    z.strictObject({
      by: z.string(), // User._id
      at: z.date(),
    })
  ),
  sys_migration: z.optional(z.string()),
});

const contactSchema = z.sctrictObject({
  name: z.string(),
  age: z.number().int().positive()
});

const ContactCollection = new Mongo.Collection<UserType>('contact');

ContactCollection.withSchema(contactSchema);

ContactCollection.withDates({
  createdAt: 'meta_created.at',
  updatedAt: 'meta_updated.at'
 })
  1. Same as insert, you could add a field for the userId who updated the document (meta_updated.by in our codebase).

  2. Same logic for the soft delete, providing more informations than just a boolean.

Let me know what are you thinking.
We will try the validation of your package when more mongo operator will be functional (we use $pop and $pull extensively).

Hey @kirafi,

Actually using zod with collection2 is not that hard, I was using it before. But I preferred to move away from collection2 and simpl-schema.

  1. I think it wont be that hard to add support for custom fields for dates, user and softDeletes. Ill check it out later.
  2. I dont like that much the updatedBy field, because usually a document is updated multiple times and this field will only store the last user that updated the doc. What we do on our platforms is create like an audit log that saves all operations and who did them. Ill may create another package that adds that functionality in the future.
  3. That may be easily doable, ill try to add it in later.
  4. About mongo operations support. The unsupported operators are not validated but work as expected, so it should be an issue if you are already not validating your mongo ops.
  5. The only issue right now that might be blobking is that the package not yet supports dot notation. I will be working on it.

For the updatedBy I think it really depend on the business, in some app it can be usefull to have that kind of log (where the full audit log is overkill).
I like to have it just to uniformize with createdBy.
Maybe it can be an optional configuration ?

Dot notation is in complicated but it will be the banger functionality for sure !
Zod schema can be really tricky with union type etc… I hope you will adress it someday.

Published version 1.0.4

  • Added support for dot notation on update ops (insert and upsert not supported yet)
  • Added support for $pop

Tests have been added for both, but cases might be still limited. So test it and let me know. $pull will be much harder as it uses conditions to pull from an array, so for the moment I will not work on it. Ill add support for $pullAll, $ and $[] next.

2 Likes

What we do on our platforms is create like an audit log that saves all operations and who did them. Ill may create another package that adds that functionality in the future.

This would be fantastic. I’ve only written this feature once and it was a pain. I funneled all updates through specific functions which looked up schemas for field names etc and then used jsondiff to show what changed. The result is good but I’ve been looking for a better way to do it in another, bigger, project. So if you want testers for your package :wink:

2 Likes