npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

machiavelli

v2.0.4

Published

Application-level schema.

Downloads

23

Readme

machiavelli

Build Status codecov

Lightweight application-level schema for Javascript models.

Why not Mongoose or another ORM

ORMs don't cover completely the features that the official drivers have. Furthermore, ORMs are notorious for being slow compared to official drivers, and tend to be irregularly maintained as time goes on to be more prone to bugs.

This module is made for those who are frustrated by reading two docs to communicate with one database. Having a separate Schema from built-in database schemas can be advantageous; data can be validated directly from your application instead of unnecessarily over network requests.

Getting Started

Don't be disheartened by all the constructors, they are there to modularize the logic and allow easy implementation of nested validation.

var Schema = require('machiavelli').Schema
var Field = Schema.Field;
var postingSchema = new Schema({
    title: new Field({ type: String }),
    price: new Field({ type: Number }),
    description: new Field({ type: String, required: false })
});

Synchronous validation

var postingDocument = { title: 'Old windows mouse', price: 20 };
if (postingSchema.isValid(postingDocument)) {
    /* save posting to database */
} else {
    /* handle error */ 
}

Asynchronous validation

postingSchema.validate(posting, function (err, capturedPosting) {
    if (err) {
        /* handle error */
    } else {
        /* save posting to database, 
         * note that capturedPosting will have only the attributes
         * specified in the schema */
    }
});

Schema inheritance

The inherits method need to be declared after schema declaration. Will not overwrite existing fields, but will inherit validation requirements of other fields.

var ticketSchema = new Schema({ 
    artist: new Field({ type: String })
});
ticketSchema.inherits(postingSchema);

Extending schemas

A schema can be extended with new fields using the method addField()

ticketSchema.add({
    venue: new Field({ type: String, required: false }), 
    anotherField: new Field({ type: Number })
});

Why use a Schema at all

Light-weight schemas can provide another protection layer for malicious database injections.

Using DataType

Required is now true by default.

var DataType = require('machiavelli').DataType;
var ticketSchema = new Schema({
    _id: new Field({ type: DataType.ObjectID }),
    title: new Field({ type: DataType.String }), 
    price: new Field({ type: DataType.Integer, required: false }), 
    createdAt: new Field({ type: DataType.Date}),
    owner: {
        _id: new Field({ type: DataType.ObjectID }),
        username: new Field({ type: DataType.String })
    }
});

Machiavelli-Defined Types

  • Function
  • String
  • Integer
  • Double
  • Date
  • Boolean
  • Array

Defining new data types

Simply define a function that returns true if object is of type data type, else returns false. Here we define a new DataType called Coordinate.

DataType.Coordinate = new DataType(function (coord) {
    var longitude = coord[0];
    var latitude = coord[1];
    
    return (longitude >= -180 && longitude <= 180) && 
        (latitude >= -90 && latitude <= 90)
});

Your own wrappers

If your data is already wrapped in your own constructor, there is no need to define a new function.

var Animal = function (specie) { this.specie = specie }
var zooSchema = new Schema({ 
    animal: new Field({ type: Animal })
});

Collection Validation @notImplemented

DataType.isValidCollection() validates type on a collection level

Optional keys

We have seen the the 'required' field, a boolean determining if Schema should fail if the field is not specified. Specifying the 'required' field, unlike the 'type' field, is optional since the default is set to true. There are many other arguments we could use to enhance our schema.

validators - Custom validators

The argument to 'validate' should always be in an array, even if it only contains one element.

Functions specified under the validate argument should return a boolean that specifies whether or not the data is valid. Custom validators can be made by the Validator constructor which takes in arguments isValid as your validation function and error as your error throwing object.

var isPositive = function (price) { return price >= 0; };
var Validator = Field.Validator;
var smallerThanTen = new Validator({ 
    isValid: function(price) { return price < 10 },
    error: new Error('Value not smaller than 10')
});

var coffeeSchema = new Schema({
    flavor: new Field({ type: String }),
    price: new Field({ 
        type: Number,
        validators: [isPostive, smallerThanTen] 
    });
});

required - Default is true

Specify required to be false if you want the Schema to not require the field in a document. If value passed to 'default' is a function, the schema will invocate the field's value at runtime.

var dateSchema = new Schema({
    date: new Field({
       type: Number,
       default: Date.now
    });
});

default

Specify default value for capture function to pass in second argument of validate.

Constructor tree

Accessing the constructors from the module. They are organized less for accessibility than for logic. You will never need to use Validator without using Field, and never Field without using Schema. As for DataType, I found it can be used without Schema.

require('machivelli')
    .Schema
        .Field
            .Validator
    .DataType

2.0.4

Nested schemas

Schemas can now specify attributes as nested schemas

var helloSchema = {
    world: worldSchema
}

Philosophy

Why so many constructors? While JavaScript is a dynamically-typed language, it can be extremely beneficial to separate concerns by using constructors as dependency injections. Hacking JavaScript can be both fast AND reliable!