javascript-models

1.0.2 • Public • Published

Codeship Status for IcaliaLabs/javascript-models Made with Love by Icalia Labs

JsModels facilitates the use of models in your application.

About

JsModels helps you abstract the layer of your app responsible for representing business data and logic.

JsModels facilitates the creation of a model with CRUD methods wired up to a method to make the API Request.

It also let you validate your model before making the API request with some out of the box validations.

const { JsModel } = require('jsmodels')
const PersonApi = require("./PersonApi")
 
class Person extends JsModel {
  static API_CLASS() {
    return PersonApi
  }
 
  static ATTRIBUTES() {
    return ["id", "name", "occupation"]
  }
}
 
const person = new Person
person.name = "Walter Reyes"
person.occupation = "Icalia Labs Coder"
 
person.save().then((response) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

Installation

npm install javascript-models

Table of Contents

Creating a JsModel

  1. Create a Model Class and inherit form JsModel
  2. Create a Model Api Class and implement the necessary methods.
const PersonApi = require("./PersonApi")
 
class Person extends JsModel {
  static API_CLASS() {
    return PersonApi
  }
}

Filling the methods in your API Class

This is the template of all the current methods wired to the API class.

Each method implementation should return a Promise to work.

If the method is not implemented, or implemented incorrectly JsModel with handle it gracefully telling you what's not working with a rejected promise.

class PersonApi {
  // Wired up to `Person.create(params)` and `person.save()`
  static postCreate(params) {...}
 
  // Wired up to `person.update()`
  static putUpdate(params) {...}
 
  // Wired up to `Person.updateAll(params)`
  static putUpdateAll(params) {...}
 
  // Wired up to `person.destroy()`
  static deleteDestroy(params) {...}
 
  // Wired up to `Person.find(params)`
  static getFind(params) {...}
 
  // Wired up to `Person.all(params)`
  static getAll(params) {...}
 
  // Wired up to `Person.first(params)`
  static getFirst(params) {...}
 
  // Wired up to `Person.findBy(params)`
  static getFindBy(params) {...}
 
  // Wired up to `Person.where(params)`
  static getWhere(params) {...}

Generator

You can always use the JsModel generator to autogenerate your Model Class and Model Api Class.

First install the generator and the simply run:

jsmodels generate <ModelName>

You should see a new models/ directory and two new files inside it:

  1. The Model file -> models/ModelName.js
  2. The API file -> models/ModelNameApi.js

You can also customize the paths for your models and api.

The Model Attributes

You need to specify the attributes your model is allowed to have.

Simply override the static method ATTRIBUTES.

class Person extends JsModel {
  static ATTRIBUTES(){
    return ["id", "name", "occupation"]
  }
}

Now when you call the constructor it will initialize a hash with the attributes you setted up.

const person = new Person()
console.log(person.modelParams()) // {id: null, name: null, occupation: null}

Also if you send params to the constructor it will only allow the attributes in ATTRIBUTES

const person = new Person({id: 1, name: 'Walter', age: 24})
console.log(person.modelParams()) // {id: 1, name: 'Walter', occupation: null}

CRUD: Reading and Writing Data

To use the following methods you need to implement how your API will handle each request.

If your API does'nt implement some methods, you don't need to use them and if you do JsModel will tell you that the method is not properly implemented.

Create

JsModels can be created from a hash with the create method and it will return the promise returned from the API postCreate method.

personParams = {
  id: 1,
  name: 'Walter',
  occupation: 'Coder',
  age: 23
}
 
Person.create(personParams).then((response) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

Using the new keyword, an object can be instantiated without being saved.

let person = new Person()
person.name = 'Walter'
person.occupation = 'Coder'

You could also set the params with the constructor

person = new Person(personParams)
console.log(person.name) // Walter

A call to person.save() will call postCreate on the PersonApi with the instance params.

person.save().then((response) => {
  console.log(response) //Response from the api
})
.catch((error) => {
  console.log(error) //Error from the api
})

Read

JsModel provides some wired methods to access your models.

Each method is wired up to a method in the API class. You would need to fill those methods to use them properly.

.find

Model.find is wired up to the method getFind in the ModelApi.

Person.find(queryParams).then((reponse) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

.all

Model.all is wired up to the method getAll in the ModelApi.

Person.all(queryParams).then((reponse) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

.first

Model.first is wired up to the method getFirst in the ModelApi.

Person.first(queryParams).then((reponse) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

.findBy

Model.findBy is wired up to the method getAll in the ModelApi.

Person.findBy(queryParams).then((reponse) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

.where

Model.where is wired up to the method getAll in the ModelApi.

Person.where(queryParams).then((reponse) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

Update

You can retrieve an instance and then call update to call the method putUpdate with the instance params.

const person = Person.findBy({name: 'Walter'})
person.name = 'David'
person.update().then((response) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

A shorthand for this is to use a hash mapping attribute names to the desired value, like so:

const person = Person.findBy({name: 'Walter'})
person.update({name: 'David'}).then((response) => {
  console.log(response)
}).catch((error) => {
  console.log(error)
})

This is most useful when updating several attributes at once, if you would like to update them in bulk you could call updateAll

Person.updateAll({name: 'David'})

Delete

Likewise, once retrieved an JsModel can be destroyed calling destroy() which will call deleteDestroy(instanceParams) on the api.

const person = Person.find_by({name: 'Walter'})
person.destroy().then((response) => {
  console.log(response)
}).catch((response) => {
  console.log(response)
})

Validations

JsModel let you validate the state of a model before making the API request.

We include many methods that you can use to check your models and validate that an attribute value is not empty, follow a specific format, has some length and many more.

All writable operations, except of destroy, call the validations before the API request.

So create, save, update, updateAll will call the validations before making api request.

You can also check that a model is valid calling isValid() or isInvalid() on an instance.

Setting Validations

class Person extends JsModel {
  ...
  static ATTRIBUTES() {
    return ["id", "name", "occupation", "email"]
  }
 
  static VALIDATIONS() {
    return {
      name: {
              length: {min: 4, max: 16}
            },
      email: {
              email: {}
            }
    }
  }
}

This would check that the name has a length is between the range of 4 and 16 and the email has the correct format.

const person = new Person()
person.name = 'Walter'
person.email = 'not-a-valid-email'
person.save().then((response) => {
 
}).catch((error) => {
  const personWithErrors = error.data.model
  console.log(personWithErrors.errors) // {email: ["must have an email format"]}
})

The Response returned from the validations has the following format:

{
  type: 'error',
  status: 0,
  ok: false,
  data: {
    error: 'Model is not valid',
    model: instanceOfModelWithErrors
  }
}
 

The

You can check the full list of available validations here

Custom Validators

Custom Validators are classes that allow you to write customized validations for your model.

They need to implement a Dictionary of Validations and a set of validations function that will be used in your Model.

class PersonCustomValidator {
  static VALIDATIONS_DICTIONARY() {
    return {
      legalMexicanAdult: this.legalMexicanAdult,
      validationWithParams: this.validationWithParams
    }
  }
 
  static legalMexicanAdult(age, args = {}) {
    return age > 18;
  }
 
  static validationWithParams(value, args = {}) {
    console.log(args)
    return true;
  }
}

and then you simply specify that your model uses this Custom Validator

class Person extends JsModel {
  static CUSTOM_VALIDATOR() {
    return PersonCustomValidator
  }
 
  static ATTRIBUTES() {
    return ["id", "name", "occupation", "email", "age"]
  }
 
  static VALIDATIONS() {
    return {
      age:  {  
              legalMexicanAdult: {}
            }
      name: {
              validationWithParams: {param: 1, param: 2}
            },
 
    }
  }
}

and that's it. Then you can use legalMexicanAdult and validationWithParams in your Person Validations.

RoadMap.

Administrate: An Out of the Box Backoffice for your models.

If an application have all their business data in JsModels we know that they have a set of validations, and some standard name to read them, create them, update them and destroy them.

With these information we could provide an fully function backoffice out of the box for your models.

And even better: backend agnostic. You could implement your API Request with whatever backend you are using: Rails, Firebase, Django, etc.

Handle relationships.

Currently you could add a foreign key as an attribute and fetch the relationships of your model.

But it would be great if JsModel could provide the mechanism to fetch those associations out of the box. Given the proper API implementations.

A Rails Backend Scaffold for your models.

Rails could provide a scaffold for your models that implements all of the available requests JsModels using some standard like JSON API

This way it would be way easier to make an API and connect it with a Client Side App

Dependents (1)

Package Sidebar

Install

npm i javascript-models

Weekly Downloads

4

Version

1.0.2

License

ISC

Last publish

Collaborators

  • walreyes