Introducing typed:model - Zod-Validated, Type-Safe MongoDB Collections for Meteor

Hello Meteor community,

I’m excited to announce the v1.0.0 release of typed:model - a Meteor package that brings automatic runtime validation and full TypeScript type safety to your MongoDB collections using Zod schemas.

Some of you may already know about this package from me talking about it in discord, but for those of you that haven’t here is the run down with a lot of new stuff for the folks in the know already.

About typed:model

typed:model is a lightweight wrapper around Mongo.Collection that provides:

  • Full TypeScript type inference for queries, inserts, and updates
  • Automatic Zod schema validation on all operations
  • Zero boilerplate - auto-populated timestamps, user tracking, and IDs
  • Smart updates - MongoDB operators with schema validation
  • Field projections - return types automatically narrow based on selection
  • Protected fields - denyUntrusted() helper prevents client side privilege escalation
  • Isomorphic database - allows client operations in a safe way. As MDG and God intended. :joy:

Quick Example

  import { Model, CustomTypes, SchemaHelpers } from 'meteor/typed:model';
  import { z } from 'zod';

  const { nonEmptyString } = CustomTypes;
  const { withCommon } = SchemaHelpers;

  // Define your schema
  const TaskSchema = withCommon(z.object({
    title: nonEmptyString,
    completed: z.boolean().default(false),
    priority: z.number().int().min(1).max(5).default(3),
  }));

  // Create a Model
  export const TaskModel = new Model({
    name: 'tasks',
    schema: TaskSchema,
  });

  // Use it with full type safety!
  const taskId = await TaskModel.insertAsync({
    title: 'Learn typed:model',
    completed: false,
    // createdAt, updatedAt, createdBy, updatedBy auto-populated!
  });

  const task = await TaskModel.findOneAsync(taskId);
  console.log(task.title); // TypeScript knows all fields!

Key Features

  1. Automatic Runtime Validation

All operations are validated against your Zod schema:

  // ✅ Valid - passes validation
  await TaskModel.insertAsync({
    title: 'Valid task',
    completed: false,
  });

  // ❌ Invalid - throws ZodError
  await TaskModel.insertAsync({
    title: '', // nonEmptyString fails
    completed: 'not a boolean', // wrong type
  });
  1. Security Built-In: Protected Fields

Prevent client side field updates with the denyUntrusted() helper:

  const UserSchema = z.object({
    username: nonEmptyString,
    email: nonEmptyString,

    // These fields are automatically protected from ALL client modifications
    isAdmin: denyUntrusted(z.boolean().default(false)),
    role: denyUntrusted(z.enum(['user', 'moderator', 'admin']).default('user')),
  });

  const UserModel = new Model({ name: 'users', schema: UserSchema });

  // CLIENT: This will be denied
  await UserModel.insertAsync({
    username: 'hacker',
    email: 'hacker@example.com',
    isAdmin: true, // Blocked! Cannot set protected field from client
  });

  // SERVER: Server can set protected fields
  await UserModel.insertAsync({
    username: 'admin',
    email: 'admin@example.com',
    isAdmin: true, // Allowed on server
  });

Schema helpers (withCommon, withTimestamps, withUsers) automatically protect their
fields too!

  1. MongoDB Operators with Validation

All update operators are validated:

  await TaskModel.updateAsync(taskId, {
    $set: { title: 'Updated Title' },
    $inc: { priority: 1 },
    $push: { tags: 'urgent' },
  });
  1. Smart Type Inference

Field projections automatically narrow return types:

  const titleOnly = await TaskModel.findOneAsync(
    { _id: taskId },
    { fields: { title: 1 } }
  );
  // titleOnly has type: { _id: string, title: string }

Installation

meteor add typed:model
meteor npm install zod

Documentation

Comprehensive docs are available:

Why Zod?

  • Modern TypeScript-first validation library
  • Composable schemas - easy to extend and reuse
  • Great error messages - detailed validation feedback
  • Active development - large ecosystem and community

GitHub Repository

GitHub - copleykj/meteor-typed-model: Create typed and validated models for you MeteorJS app

Try It Out!

I’d love to hear your feedback! Please try it in your projects and let me know:

  • What features you’d like to see
  • Any bugs or issues you encounter

Contributions are very welcome! The package has comprehensive test coverage (116 tests) and detailed documentation.

Attribution

The basis of this package is code extracted from the GitHub - deathandmayhem/jolly-roger: Dead men tell no tales! project created by Evan Broder, adapted and packaged for use in any project.


Looking forward to hearing your thoughts!

9 Likes

Ah, this is splendid news! I dare say we’re about to enter into a golden age of Meteor development, as we refactor all of the legacy packages to modern equivalents.

aldeed:collection2 served us well for a decade, but I’m 100% behind Zod and native JSONSchema support on the data cursors. Great job on the rewrite!

2 Likes

Thanks! :slight_smile:

I’m definitely a huge fan of collection2 and simple-schema. I made extensive use of them in the socialize packages and they served me well. That being said I’ve become a bit of a type safety zealot after working with a lot of the broader JS ecosystem, and these packages don’t provide the level of typing that I’ve come to expect in my code these days.

2 Likes

Just as @jkuester is working on adding support for Zod into collection2

1 Like

Actually I just opened C2 for other schemas and @harry97 is implementing the Zod support.

2 Likes

Yeah actually seeing this post made me a :pinching_hand: bit jealous. We gotta be quick with it lol. But I’m very glad actually that the community is picking up and moving things :clap:

Great work @copleykj