Introduction

This is part of a series on some not-so-common Meteor mistakes. For some background, see the first post. I promised to tell a joke rather than another write another parable, but I haven’t thought of one yet. Any ideas?

The Story

You’re writing a blogging application using Meteor and decide to have the title of your posts automatically converted to nice slug to be used in the url. You already are using SimpleSchema and Collection2 to keep your data structured. You remember reading about the auto-values in the documentation and decide that is a great solution. You type up the following and commit it into source-control.

PostSchema = new SimpleSchema({
  title: {
    type: String,
  },
  slug: {
    type: String,
    autoValue: function() {
      const title = this.field('title').value;
      return title.toLowerCase().replace(/ /g,'-');
    },
  },
});

You publish a few posts in the following days and are very happy when several large sites link to your posts. You noticed you forgot to capitalize the title in one of your posts and realize you need to be able to edit the title after a post has been published. Since your post is enormously popular many sites have links pointing to the url so you must be careful not to change the slug, even if you change the title. You quickly decide on adding a published field to your schema. You want the slug to match the title if you are still drafting up the post, but not once it is published.

PostSchema = new SimpleSchema({
  title: {
    type: String,
  },
  published: {
    type: Boolean,
    defaultValue: false,
  },
  slug: {
    type: String,
    autoValue: function() {
      if (this.field('published').value) {
        return; // don't change slug 
      }
      const title = this.field('title').value;
      return title.toLowerCase().replace(/ /g,'-');
    },
  },
});

In addition, you add a method to allow you to edit the title from the client. You quickly push the changes into production, correct the title of your last post and call it a day. You walk into work the following day and see that the traffic to that post dropped to almost nothing. You realize that the url isn’t the same as it was the day before.

The Code

Here is a simplified version of the method called when the title is changed. Can you see why a post’s slug would change, despite the published field being true?

Meteor.methods({
  editTitle(postId, title) {
    // some permission checks...
    Posts.update(postId, {$set: {title}});
  },
})

When the title is changed the method associated with the slug is called, however the publish field isn’t actually in the modifier, hence the if block isn’t executed.

The Analysis

This could have been prevented if any of the following had been done differently.

1) Always err to the lesser evil. Changing a published post’s slug is definitely worse than not changing a draft’s post slug. This version would have been cleaner, avoided the more serious error but nevertheless is still incorrect.

function() {
  if (this.field('published').value === false) {
    const title = this.field('title').value;
    return title.toLowerCase().replace(/ /g,'-');
  }
}

2) Two simple tests would have caught the error in both versions.

3) Don’t rely too much on SimpleSchema. Mongo 3.2 introduced database level schemas and it is something to consider. I’ll write a post on that the if there is interest.

The Conclusion

SimpleSchema is a great package but it runs at the application level and as such, has its limits. When using SimpleSchema don’t rely on AutoValue to enforce constraints across fields.