Are `Match` and `check` here to stay? Is a new alternative in the works?

Are Match and check here to stay or are they going away in a future version of Meteor? Is a new alternative in the works?

Continuing the discussion from Where's Match.And?:

export default
class Schema {
    constructor(spec) {
        this.spec = spec || {}
    }

    applyTo(collection) {
        collection.before.insert((userId, doc) => {
            _checkDocument(this, doc, collection, 'insert')
        })
        collection.before.update((userId, doc, fieldNames, modifier, options) => {
            _checkDocument(this, doc, collection, 'updat')
        })
        collection.before.upsert(function(userId, doc, fieldNames, modifier, options) {
            // TODO
        })
    }
}

function _checkDocument(self, document, collection, action) {
    try {
        check(document, Object.assign({
            _id: String, // TODO: make a MeteorIDString Match type.
        }, self.spec))
    }
    catch(error) {
        throw new Error(`${error.message} while ${action}ing item ${action == 'insert' ? 'into' : 'in'} '${collection._name}' collection.`)
    }
}

In the guide we’re recommending SimpleSchema, and have designed a new error format for validation errors that we hope can be adopted by other libraries.

SimpleSchema is a bit more verbose, but more easily extensible and has a lot more useful features. We hope that people can eventually write a wrapper package to generate schemas with a more concise syntax if they prefer that syntax.

See an example here: http://guide.meteor.com/collections.html

Oh I just realized I totally forgot to mention the justification for this:

  1. Many more types of validation available in simple-schema, like length of strings, min/max for numbers, integers vs. decimals, etc.
  2. Ability to easily report errors for each field individually
  3. Compatibility with useful packages like collection2 and autoform

We think it would be best to have just one “language” for specifying types and schemas that can be used across forms, collections, methods, publications, and more. Right now the situation was that people were using check for some cases and simple-schema for others.

The only things that are downsides for SimpleSchema:

  1. Slower
  2. More verbose

But I believe these could be easily fixed.

@sashko

One more downside is that, if we find a use case that isn’t supported by SimpleSchema yet, then we’re out of luck (at least based on what we can do with the API described in the formal docs), which is why I like found Match and check to be more flexible: we can simply write a Match.Where function to handle the case.

We can even store the Match.Where clause in a separate file, give it a name, then import it, f.e.:

const ID_REGEX = /^[23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz]{17}$/

export const MeteorIDString = Match.Where(function(value) {
    if (!Match.test(value, String) || !value.match(ID_REGEX))
        throw new Match.Error('Value '+value+' is not a Meteor.Collection _id string.')
    return true
})
import MeteorIDString from './validation'
import Schema from './utilities/Schema'

let Buckets = new Mongo.Collection('Buckets')

new Schema({
  things: [MeteorIDString] // array of `MeteorIDString`s
}).applyTo(Buckets)

How do you do the case you’re talking about with check?

I think this is just a bit of friction between the way SimpleSchema and check are meant to be used:

function func(a, b, c) {

// The check way: call for each argument
check(a, pattern);
check(b, pattern);
check(c, pattern);

// The simple-schema way: call once for all arguments
new SimpleSchema({
  a: pattern,
  b: pattern,
  c: pattern
}).validate({ a, b, c });

I think it’s possible to define a custom validator for a single simple-schema property.

Note that this method is what lets you get errors about individual fields, since simple-schema will actually be aware of the names of the properties.

I’m using check() / Match() in combination with SimpleSchema and really like the combination of the two. If I need quick checks, the simple check syntax comes in handy. If the check needs to be more sophisticated, I use SimpleSchema for it. But this happens only seldomly, so I would say SimpleSchema is a bit overweighted for “just” the checks. I love it for database validation, though. Could not live without it for this. Can’t imagine how many errors I would have introduced accidentally if I had no validation in place. Especially if you refactor your app, SimpleSchema (and check) are invaluable tools to hint for any missing refactorings.

Right I mean I think it’s pretty trivial to write a wrapper package that converts:

simpleCheck(obj, {
  a: String,
  b: Number,
  c: { type: Number, decimal: true}
});

to

new SimpleSchema({
  a: { type: String },
  b: { type: Number },
  c: { type: Number, decimal: true }
}).validate(obj);

Would that make it look more “lightweight”?

Another big benefit is that now you can be 100% sure that your argument checks and your schema checks use the same logic - for example, check(x, Number} allows any number while simple-schema Number only accepts integers by default.

It could be that I don’t know SimpleSchema well enough (probably the docs didn’t describe everything I needed at the time). I was getting this error for no particularly obvious reason. When I switched to using Match/check, it just worked.

At that point in time it was also my first time learning Match/check after having tried SimpleSchema, so I see it’s possible to use the custom in a SimpleSchema, and then use Match/check inside of that.

In my case, the descriptive nature of Match/check used to describe the structure of an object was plenty and flexible enough for my use cases.

That may be counter-intuitive to what Number means in JavaScript.

1 Like

Fair point. On the other hand, check has no sane way to check for integers at all.

We could definitely compile a wish list of changes to SimpleSchema syntax/defaults and get @aldeed to take a look.

Yes, I’ve run into that as well. I think it would be best if collection2 “cleaning” didn’t silently drop fields that aren’t part of the schema, and threw an error instead.

Here’s how it can be done with Match:

const Integer = Match.Where(value => Number.isInteger()) // es2015, but could be done in ES5 with Math.floor

new Schema({
  age: Integer
}).applyTo(collection)
1 Like

@aldeed @sashko What about perhaps some way to make SimpleSchema more friendly to Match? For example, maybe allow SImpleSchema’s way and also just allow providing a Match for a key?

Current way:

BookSchema = new SimpleSchema({
  title: {
    type: String,
    max: 200
  }
})

alternative:

import {MeteorIDString} from './validation' // (see above example)
import {Email} from './validation' // imagine some Email Match.Where clause.

const stringWithMaxLengh = length =>
  Match.Where(value => typeof value == 'string' && value.length <= length)

BookSchema = new SimpleSchema({
  title: stringWithMaxLength(200),
  authors: [Match.OneOf(Email, MeteorIDString)] // online books, uses email or ID to relate to author.
})

Yes, the biggest ‘weight’ problem is the syntax used for nesting objects. Writing this is not really convenient:

parent: {
  type: Object
},
'parent.child': {
  type: String
}

so your wrapper might be a good approach to make this easier.

1 Like

Yeah I feel like most of the concerns people are bringing up are either with the end-user syntax or Collection2 integration.

Perhaps we should start making a list of things that could be improved in both for the next version!

Oh gosh, why do this?

What makes things even worse, it also cleans the objects you pass into the validation process. So it can happen that your surrounding code runs into issues because fields got lost. This was not obvious to me, and I ran into this trap once. Quite hard to track this down.

If you should work with @aldeed to improve SimpleSchema: My dearest wish would be to have better / more explanatory error messages. For instance, if an error occurs in an array, you get a strange message that field [0] caused a problem (or something like that). It took me a while to figure out what happened. :smile:

It’s just a contrived example. It just shows something that is possible using Meteor’s Match API (I haven’t actually ever used Match.OneOf in an array like that before), the main point being that Match and check could be flexible or desirable enough to use, with shorter syntax (not needing more than one key in a schema to define the value of a key as @waldgeist mentioned).

1 Like

Hmm it could be hard to dig these up at this point, but I bet it would be super useful to make a list of all of these “edge cases” so that we can address them directly.

Yup. But as I said, from a high-level perspective I have to say that SimpleSchema is a true life-saver. Couldn’t work without it.

PS: Any reason why audit-argument-checks was removed in the latest version of the todo app? This package is super-important for detecting errors, and I was just curious why you “temporarily removed” it?

1 Like