node package manager
Loosely couple your services. Use Orgs to version and reuse your code. Create a free org »

@nmehta6/morpheus

Morpheus

A simple little library to validate and transform objects. It is inspired by the awesome .NET library AutoMapper but built for JavaScript.

Note: This is an experimental package. Please file an issue if you find one.

Build Status Dependency Status Coverage Status

morpheus

Installation

Using Bower

bower install morpheus --save

In a browser:

<script src="bower_components/morpheus/dist/morpheus.js"></script>

Using npm

npm install @nmehta6/morpheus --save

In Node.js/io.js

let Morpheus = require('morpheus')
let morpheus = new Morpheus()

Overview

Morpheus uses JSONSchema to work with objects in a "typed" way. There is a two step process.

  1. Register: register all mappings (typically done when your app is bootstrapping). fromSchema and toSchema are JavaScript objects that comply with the JSONSchema spec.
  2. Use one of the registered mappings to transform.

Register example:

let fromSchema = {
    type: 'object',
  properties: {
    name: { type: 'string' },
    address: { type: 'string' }
  }
}
 
let toSchema = {
    type: 'object',
    properties: {
        address: { type: 'string' }
    }
}
let = morpheus = new Morpheus()
 
morpheus.register({
    id: 'neo',
    fromSchema: fromSchema,
    toSchema: toSchema
})

Map example:

let fromObj = { name: 'Mr. Anderson', address: 'Capital City, USA' }
 
let actual = morpheus.map('new', fromObj)
expect(actual)
    .to.have.property('name').equal('Mr. Anderson')
let isValid = morpheus.validate(actual, toSchema)
expect(isValid.errors).to.have.length(0)

Features

Note: Examples are taken from unit tests located in ./test

Validation with JSONSchema

Validation is enforced on both fromObj and toObj using the fromSchema and toSchema. If you are calling an external service and if the service changes the data model, you can get a validation error early. It also makes writing unit tests easier for the mapping logic.

let schema = {
    type: 'number'
}
let instance = 4
 
let actual = morpheus.validate(instance, schema)
expect(actual.errors).to.have.length(0)

Map Arrays

Mapping arrays and transforming them

let fromSchema = {
    type: 'array',
    items: {
        type: 'number'
    }
}
let toSchema = {
    type: 'array',
    items: {
        type: 'number'
    },
    morph: x => x.map(y => y * 2)
}
let fromArray = [1, 2, 3]
let actual = morpheus.map('array', fromArray)
//actual is now [2,4,6] 

Map objects

Mapping objects with desired properties

let fromSchema = {
    type: 'object',
    properties: {
        name: { type: 'string' },
        address: { type: 'string' },
        zip: { type: 'string' }
    }
}
let toSchema = {
    type: 'object',
    properties: {
        name: { type: 'string' },
        zip: { type: 'string' }
    }
}

Default values

Default value for the zip property

let fromSchema = {
    type: 'object',
    properties: {
        name: { type: 'string' },
        zip: {
            type: ['string', 'null']
        }
    }
}
let toSchema = {
    type: 'object',
    properties: {
        name: { type: 'string' },
        zip: {
            type: 'string',
            'default': '60075'
        }
    }
}

Flattening

Flatten an object using the camelCase property naming convention for the addressZip property.

let fromSchema = {
    type: 'object',
    properties: {
        address: {
            type: 'object',
            properties: {
                zip: {
                    type: ['string']
                }
            }
        }
    }
}
let toSchema = {
    type: 'object',
    properties: {
        addressZip: {
            type: 'string'
        }
    }
}

Projection

Project over properties of an object and create new properties

let fromSchema = {
    type: 'object',
    properties: {
        firstName: {
            type: 'string'
        },
        lastName: {
            type: 'string'
        },
        zip: {
            type: 'number'
        }
    }
}
let toSchema = {
    type: 'object',
    properties: {
        name: {
            type: 'string',
            morph: x => `${x.firstName} ${x.lastName}`
        },
        zip: {
            type: 'string',
            morph: x => x.zip.toString()
        }
    }
}