node package manager

alamid-schema

Extendable mongoose-like schemas for node.js and the browser

alamid-schema

Extendable mongoose-like schemas for node.js and the browser

npm status
build status dependencies devDependency Status

If you like mongoose schemas and you want to use them standalone, alamid-schema is the right module for you.

alamid-schema helps you with

  • validation of data
  • using mongoose-like schemas without using mongoose
  • sharing data-definition between client & server
  • normalizing data (coming soon)
  • striping readable/writeable fields (coming soon)

Use it on the server to...

  • normalize and validate incoming requests
  • strip private fields from the response

Use it on the client to...

  • validate forms
  • define view models
var Schema = require("alamid-schema");
 
var Panda = new Schema("Panda", {
    name: String,
    age: {
        type: Number,
        required: true,
        writable: false,
        readable: true
    },
    mood: {
        type: String,
        enum: ["grumpy", "happy"]
    },
    birthday: Date
});

Examples

Schema Definition

You can define your schema with concrete values...

var PandaSchema = new Schema({
    name: "panda",
    age: 12,
    friends: {
        type: []
    }
});

...or with abstract types...

var PandaSchema = new Schema({
    name: String,
    age: Number,
    friends: {
        type: Array
    }
});

Extend

Sometimes you want to extend your Schema and add new properties.

var PandaSchema = new Schema({
    name: String,
    age: Number,
    friends: {
        type: Array
    }
});
 
var SuperPanda = PandaSchema.extend("SuperPanda", {
    xRay: true,
    canFly: {
        type: Boolean
    }
});

We have a superpanda now... which can fly and has xray eyes! That's basically the same as...

var SuperPanda = new Schema({
    name: String,
    age: Number,
    friends: {
        type: Array
    },
    xRay: true, //added 
    canFly: {   //added 
        type: Boolean
    }
});

Overwriting properties

If you define a property in the schema you are extending with, the extending schema takes precedence.

var Animal = new Schema({
    name: String,
    age: String
});
 
var Panda = Animal.extend("Panda", {
    age: Number
    color: String
});

equals...

var Panda = new Schema("Panda", {
    name: String,
    age: Number,   //overwritten 
    color: String  //added 
});

Plugin: Validation

The validation plugins adds - suprise! - validation support.

var Schema = require("alamid-schema");
 
Schema.use(require("alamid-schema/plugins/validation"));
 
var PandaSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    age: {
        type: Number,
        min: 9,
        max: 99
    },
    mood: {
        type: String,
        enum: ["happy", "sleepy"]
    },
    treasures: {
        type: Array,
        minLength: 3
    },
    birthday: Date
});
 
var panda = {
    name: "Hugo",
    age: 3,
    mood: "happy"
};
 
PandaSchema.validate(panda, function(validation) {
    if (!validation.result) {
        console.log(validation.errors);
        return;
    }
 
    console.log("happy panda");
});

outputs...

{
    result: false,
    errors: {
        age: [ 'min' ]
    }
}

Included validators:

  • required
  • min (works on Number)
  • max (works on Number)
  • enum
  • minLength (works on String, Array)
  • maxLength (works on String, Array)
  • hasLength (works on String, Array)
  • matches (performs a strict comparison, also accepts RegExp)

Writing custom validators:

You can write sync and async validators..

 
// sync 
function oldEnough(age) {
    return age > 18 || "too-young";
}
 
// async 
function nameIsUnique(name, callback) {
    fs.readFile(__dirname + "/names.json", function(err, names) {
        if(err) {
            throw err;
        }
 
        names = JSON.parse(names);
        callback(names.indexOf(name) === -1 || "name-already-taken");
    });
}
 
var PandaSchema = new Schema({
    name: {
        type: String,
        required: true,
        validate: nameIsUnique
    },
    age: {
        type: Number,
        validate: oldEnough,
        max: 99
    }
});
 
var panda = {
    name: "hugo",
    age: 3,
    mood: "happy"
};
 
PandaSchema.validate(panda, function(validation) {
    if(!validation.result) {
        console.log(validation.errors);
        return;
    }
    console.log("happy panda");
});

outputs...

{
    name: [ "name-already-taken" ],
    age:  [ "too-young" ]
}

Note: validators will be called with this bound to model.

Promises

The validate() method also returns a promise:

PandaSchema.validate(panda)
    .then(function (validation) {
        ...
    })

The promise will still be resolved even when the validation fails, because a failed validation is not an error, it's an expected state.

The promise provides a reference to the final validation result object. It contains the intermediate result of all synchronous validators:

var promise = PandaSchema.validate(panda);
 
if (promise.validation.result) {
    console.log("Synchronous validation of " + Object.keys(validation.errors) + " failed");
}

Important notice: You must bring your own ES6 Promise compatible polyfill!

API

Core

Schema([name: String, ]definition: Object)

Creates a new schema.

.fields: Array

The array of property names that are defined on the schema. Do not modify this array.

.only(key1: Array|String[, key2: String, key3: String, ...]): Schema

Returns a subset with the given keys of the current schema. You may pass an array with keys or just the keys as arguments.

.extend([name: String, ]definition: Object): Schema

Creates a new schema that inherits from the current schema. Field definitions are merged where appropriate. If a definition conflicts with the parent definition, the child's definition supersedes.

.strip(model: Object)

Removes all properties from model that are not defined in fields. Will not remove properties that are inherited from the prototype.

Readable & Writable fields

You can define readable and writable fields in the schema. By default, every field is read- and writable.

var PandaSchema = new Schema({
    id: {
        type: Number,
        required: true,
        readable: true,
        writable: false
    },
    lastModified: {
        readable: true,
        writable: false
    }
};

.writableFields()

Returns an array containing the keys of all writable fields:

PandaSchema.writableFields(); // ["name", "age", "mood", "treasures", "birthday"] 

.writable()

Creates a new schema that contains only the writable fields:

var PandaWritableSchema = PandaSchema.writable();

.readableFields()

Returns an array containing the keys of all readable fields:

PandaSchema.readableFields(); // ["name", "age", "mood", "treasures", "birthday"] 

.readable()

Creates a new schema that contains only the readable fields:

var PandaReadableSchema = PandaSchema.readable();

Plugin: Validation

.validate(model: Object[, callback: Function]): Promise

Validate given model using the schema definitions. Callback will be called/Promise will be fulfilled with a validation object with result (Boolean) and errors (Object) containing the error codes.