First be sure you have DataBase and Node.js installed.

Installation

Installation is done using the NodeJS Package Manager (npm). If you don't have npm installed on your system you can download it from npmjs.org To install caminte:

npm install caminte --save

Caminte CLI

Caminte's command line interface, provides creation of models, a set of development tools.

Install caminte CLI with the following command:

npm install caminte-cli -g
  Usage: caminte [options] [name] [fields]

  Options:

    -h, --help                        output usage information
    -V, --version                     output the version number
    -i, --init                        create structure and config
    -a, --adapter [name]              database adapter (mysql|redis|etc...)
    -m, --model <modelname> [fields]  create data model
    -r, --route <routename>           create data routes
    -c, --crud  <crudname>  [fields]  create model and route
    -d, --dump  <dumpfile>            parse sql dump file
    -t, --tests                       add tests
    -f, --force                       force on non-empty directory

Create structure

$ caminte -i -a mysql

Create model

$ caminte -m User active:int name email password note:text created:date
# with tests
$ caminte -t -m User active:int name email password note:text created:date

Create model and routes

$ caminte -c Post published:bool title content:text created:date
# with tests
$ caminte -t -c User active:int name email password note:text created:date

Create models and routes from SQL dump

$ caminte -d dumpfile.sql

First, we need to define a connection.

Example connections:

MySQL / MariaDB

For MySQL database need install mysql client. Then:

$ npm install mysql --save
var caminte = require('caminte'),
    Schema = caminte.Schema,
    config = {
         driver     : "mysql",    // or mariadb
         host       : "localhost",
         port       : "3306",
         username   : "test",
         password   : "test",
         database   : "test",
         pool       : true // optional for use pool directly
    };

var schema = new Schema(config.driver, config);

SQlite 3

For SQLite database need install sqlite3 client. Then:

$ npm install sqlite3 --save
var caminte = require('caminte'),
    Schema = caminte.Schema,
    config = {
         driver     : "sqlite3",
         database   : "/db/mySite.db"
    };

var schema = new Schema(config.driver, config);

PostgreSQL

For PostgreSQL database need install postgres client. Then:

$ npm install pg --save
var caminte = require('caminte'),
    Schema = caminte.Schema,
    config = {
         driver     : "postgres",
         host       : "localhost",
         port       : "5432",
         username   : "test",
         password   : "test",
         database   : "test",
         pool       : true // optional for use pool directly
    };

var schema = new Schema(config.driver, config);

Redis

For Redis database need install redis client. Then:

$ npm install redis --save
var caminte = require('caminte'),
    Schema = caminte.Schema,
    config = {
         driver     : "redis",
         host       : "localhost",
         port       : "6379",
         database   : "test"
    };

var schema = new Schema(config.driver, config);

Configuration parameters

  • object config: can contain any of the following optional properties
    • string driver:
      • default value: null
      • adapter to use when connecting to database server
    • string user:
      • default value: process.env.USER
      • Database user
    • string database:
      • default value: process.env.USER
      • database to use when connecting to database server
    • string password:
      • default value: null
      • user's password for database server
    • number port:
      • default value: depends by adapter
      • port to use when connecting to database server
    • string host:
      • default value: null
      • host address of database server
      • used to initialize underlying net.Stream()
    • bool pool:
      • default value: false
      • used pooling connections to connect to server
    • bool ssl:
      • default value: false
      • whether to try SSL/TLS to connect to server

Define Model

Models are defined through the Schema interface.

// define models
var Post = schema.define('Post', {
    title:     { type: schema.String,  limit: 255 },
    content:   { type: schema.Text },
    params:    { type: schema.JSON },
    date:      { type: schema.Date,    default: Date.now },
    published: { type: schema.Boolean, default: false, index: true }
});

// simplier way to describe model
var User = schema.define('User', {
    name:         schema.String,
    bio:          schema.Text,
    approved:     schema.Boolean,
    joinedAt:     schema.Date,
    age:          schema.Number
});

Define Indices

Add single-column index

var User = schema.define('User', {
    email:     { type: schema.String, "null": false, unique: true },
    name:      { type: schema.String, "null": false, limit: 255, index: true },
    content:   { type: schema.Text },
    params:    { type: schema.JSON },
    date:      { type: schema.Date,   default: Date.now },
});

Add multi-column index

var User = schema.define('User', {
    email:          { type: schema.String, "null": false },
    name:           { type: schema.String, "null": false },
    birthDate:      { type: schema.Date },
    pendingPeriod:  { type: schema.Number, default: 0 },
    createdByAdmin: { type: schema.Boolean, default: 1 }
}, {
    indexes: {
        idx_1: {
            columns: 'email, createdByAdmin'
        },
        idx_2: {
            columns: 'name'
        }
    }
});

Define Primary Keys

var Student = schema.define("Student", {
    stuID: { type: schema.Number },
    school: { type: schema.String }
}, {
    primaryKeys: ["stuID"]
});

Schema data types

For Relational databases

Caminte Typenames Cast in adapters
mysql postgres sqlite3 firebird
Number INTEGER INTEGER INTEGER INTEGER
Integer INTEGER INTEGER INTEGER INTEGER
Float FLOAT DOUBLE PRECISION REAL REAL
Double FLOAT DOUBLE PRECISION REAL REAL
Real REAL REAL REAL REAL
Boolean TINYINT BOOLEAN BOOLEAN BOOLEAN
Date DATETIME TIMESTAMP DATETIME DATETIME
String VARCHAR VARCHAR VARCHAR VARCHAR
Text TEXT TEXT TEXT TEXT
Json TEXT JSON TEXT TEXT
BLOB
no datatype specified
NONE NONE NONE NONE

Setup Validations

User.validatesPresenceOf('name', 'email')
User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}});
User.validatesInclusionOf('gender', {in: ['male', 'female']});
User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']});
User.validatesNumericalityOf('age', {int: true});
User.validatesUniquenessOf('email', {message: 'email is not unique'});

user.isValid(function (valid) {
    if (!valid) {
        user.errors // hash of errors {attr: [errmessage, errmessage, ...], attr: ...}
    }
})

Custom Validations

Sync

function userNameValidator(err) {
     if (this.name === 'bad') { err(); }
});

User.validate('name', userNameValidator, {message: 'Bad name'});

var user = new User({name: 'Peter'});
user.isValid(); // true
user.name = 'bad';
user.isValid(); // false

Async

function userNameValidator(err, done) {
    process.nextTick(function () {
         if (this.name === 'bad') { err(); }
         done();
    });
});

User.validateAsync('name', userNameValidator, {message: 'Bad name'});

var user = new User({name: 'Peter'});

user.isValid(); // false (because async validation setup)
user.isValid(function (isValid) {
     isValid; // true
})
user.name = 'bad';
user.isValid(); // false
user.isValid(function (isValid) {
     isValid; // false
});

Setup Relationships

User.hasMany(Post,   {as: 'posts',  foreignKey: 'userId'});
// creates instance methods:
// user.posts(conds)
// user.posts.build(data) // like new Post({userId: user.id});
// user.posts.create(data) // build and save

Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
// creates instance methods:
// post.author(callback) -- getter when called with function
// post.author() -- sync getter when called without params
// post.author(user) -- setter when called with object

// work with models:
var user = new User;
user.save(function (err) {
    var post = user.posts.build({title: 'Hello world'});
    post.save(console.log);
});

Define any Custom Method

for instance

User.prototype.getNameAndAge = function () {
    return this.name + ', ' + this.age;
};

for model

Post.myCustomMethod = function (params, callback) {
    // any code here
    Post.schema.client.query(sql, callback);
};

Define scope

Post.scope('active', { published : true });

Post.active(function(err, posts){
    // your code here
});

Just instantiate model

var post = new Post();

Query parameters

  • where {Object}
  • order {String}
  • skip {Number}
  • limit {Number}
  • group {String}

#exists(id, callback)

Check if exists instance

Post.exists(12, function(err, exists){
   // your code here
});

#create(data, callback)

Save model (of course async)

Post.create(function(err){
   // your code here
});
// same as new Post({userId: user.id});
user.posts.build
// same as Post.create({userId: user.id}, function(err){
   // your code here
// });
user.posts.create(function(err){
   // your code here
});

#findOrCreate(query, data, callback)

Find if exists or create instance.

// find user by email
User.findOrCreate({
      email : 'example@example.com'
    }, {
      name : 'Gocha',
      age : 31
    }, function(err, user){
      // your code here
});

#findOne(query, callback)

Get one latest instance

Post.findOne({where: {published: true}, order: 'date DESC'}, function(err, post){
   // your code here
});
// or
var Query = Post.findOne();
Query.where('published',true).desc('date');
Query.run({}, function(err, post){
   // your code here
});

#findById(id, callback)

Find instance by id

User.findById(1, function(err, user){
   // your code here
})

#find(query, callback)

Find instances

// all posts
Post.find(function(err, posts){
   // your code here
});

// all posts by user
var Query = Post.find();
Query.where('userId', 2);
Query.order('id', 'ASC');
Query.skip(20).limit(10);

Query.run({},function(err, posts){
   // your code here
});

// the same as prev
Post.find({where: {userId: user.id}, order: 'id', limit: 10, skip: 20}, function(err, posts){
   // your code here
});

#all(query, callback)

Get all instances

// all published posts
var Query = Post.all();
Query.where('published', true).desc('date');
Query.run({}, function(err, post){
   // your code here
});
// all posts
Post.all(function(err, posts){
   // your code here
});
// all posts by user
Post.all({where: {userId: 2}, order: 'id', limit: 10, skip: 20}, function(err, posts){
   // your code here
});
// the same as prev
user.posts(function(err, posts){
   // your code here
});

#upsert(query, data, callback)

Update if exists or create instance

Post.updateOrCreate({
      id: 100
    }, {
      title: 'Riga',
      tag: 'city'
    }, function(err, post){
      // your code here
});
// or
User.updateOrCreate({
      email: 'example@example.com'
    }, {
      name: 'Alex',
      age: 43
    }, function(err, user){
      // your code here
});

#update(query, data, callback)

Update if exists instances

User.update({
      where : {
           email: 'example@example.com'
        }
    }, {
      active: 0
    }, function(err, user){
      // your code here
});
// or
 Post.update({
       id: {
          inq: [100, 101, 102]
       }
     }, {
       tag: 'city'
     }, function(err, post){
       // your code here
 });

#count(query, callback)

Count instances

// count posts by user
Post.count({where: {userId: user.id}}, function(err, count){
   // your code here
});

#remove(query, callback)

Remove instances

// remove all unpublished posts
Post.remove({where: {published: false}},function(err){
   // your code here
});

#destroyById(id, callback)

Destroy instance by id

User.destroyById(22, function(err) {
    // your code here
});

#destroy(callback)

Destroy instance

User.findById(22, function(err, user) {
    user.destroy(function(err){
       // your code here
    });
});

#destroyAll(callback)

Destroy all instances

User.destroyAll(function(err){
   // your code here
});

Example Queries

var Query = User.find();
Query.where('active', 1);
Query.order('id DESC');
Query.run({}, function(err, users) {
   // your code here
});

#where(key, val)

var Query = User.find();
Query.where('userId', user.id);
Query.run({}, function(err, count){
   // your code here
});
// the same as prev
User.find({where: {userId: user.id}}, function(err, users){
   // your code here
});

#gt(key, val)

Specifies a greater than expression.

Query.gt('userId', 100);
Query.where('userId').gt(100);
// the same as prev
User.find({
      where: {
         userId: {
              gt : 100
         }
      }
    }}, function(err, users){
   // your code here
});

#gte(key, val)

Specifies a greater than or equal to expression.

Query.gte('userId', 100);
Query.where('userId').gte(100);
// the same as prev
User.find({
      where: {
         userId: {
              gte : 100
         }
      }
    }}, function(err, users){
   // your code here
});

#lt(key, val)

Specifies a less than expression.

Query.lt('visits', 100);
Query.where('visits').lt(100);
// the same as prev
Post.find({
      where: {
         visits: {
              lt : 100
         }
      }
    }}, function(err, posts){
   // your code here
});

#lte(key, val)

Specifies a less than or equal to expression.

Query.lte('visits', 100);
Query.where('visits').lte(100);
// the same as prev
Post.find({
      where: {
         visits: {
              lte : 100
         }
      }
    }}, function(err, posts){
   // your code here
});

#ne(key, val)

Matches all values that are not equal to the value specified in the query.

Query.ne('userId', 100);
Query.where('userId').ne(100);
// the same as prev
User.find({
      where: {
         userId: {
              ne : 100
         }
      }
    }}, function(err, users){
   // your code here
});

#in(key, val)

Matches any of the values that exist in an array specified in the query.

Query.in('userId', [1,5,7,9]);
Query.where('userId').in([1,5,7,9]);
// the same as prev
User.find({
      where: {
         userId: {
              in : [1,5,7,9]
         }
      }
    }}, function(err, users){
   // your code here
});

#regex(key, val)

Selects rows where values match a specified regular expression.

Query.regex('title', 'intel');
Query.where('title').regex('intel');
// the same as prev
Post.find({
      where: {
         title: {
              regex : 'intel'
         }
      }
    }}, function(err, posts){
   // your code here
});

#like(key, val)

Pattern matching using a simple regular expression comparison.

Query.like('title', 'intel');
// the same as prev
Post.find({
      where: {
         title: {
              like : 'intel'
         }
      }
    }}, function(err, posts){
   // your code here
});

#nlike(key, val)

Pattern not matching using a simple regular expression comparison.

Query.nlike('title', 'intel');
// the same as prev
Post.find({
      where: {
         title: {
              nlike : 'intel'
         }
      }
    }}, function(err, posts){
   // your code here
});

#nin(key, val)

Matches values that do not exist in an array specified to the query.

Query.nin('id', [1,2,3]);
// the same as prev
Post.find({
      where: {
          title : {
                   nin : [1,2,3]
          }
      }
    }}, function(err, posts){
   // your code here
});

#sort(key, val)

Sets the sort column and direction.

Query.sort('title DESC');
Query.sort('title', 'DESC');
// the same as prev
Post.find({
      order: 'title DESC'
    }}, function(err, posts){
   // your code here
});

#group(key)

Sets the group by column.

Query.group('title');
// is the same as
Post.find({
      group: 'title'
    }}, function(err, posts){
   // your code here
});

#asc(key)

Sets the sort column and direction ASC.

Query.asc('title');
// is the same as
Query.sort('title ASC');
// the same as prev
Post.find({
      order: 'title ASC'
    }}, function(err, posts){
   // your code here
});

#desc(key)

Sets the sort column and direction DESC.

Query.desc('title');
// is the same as
Query.sort('title DESC');
// the same as prev
Post.find({
      order: 'title DESC'
    }}, function(err, posts){
   // your code here
});

#skip(val)

The skip method specifies at which row the database should begin returning results.

Query.skip(10);
// the same as prev
Post.find({
      skip: 10
    }}, function(err, posts){
   // your code here
});

#limit(val)

The limit method specifies the max number of rows to return.

Query.limit(10);
// the same as prev
Post.find({
      limit: 10
    }}, function(err, posts){
   // your code here
});

#slice(val[])

Limits the number of elements projected from an array. Supports skip and limit slices.

Query.slice([20,10]);
// the same as prev
Post.find({
      skip: 20,
      limit: 10
    }}, function(err, posts){
   // your code here
});

#between(key, val[])

Check whether a value is within a range of values.

Query.between('created', ['2013-01-01','2013-01-08']);
// the same as prev
Post.find({
      where: {
         created: {
            between : ['2013-01-01','2013-01-08']
         }
      }
    }}, function(err, posts){
   // your code here
});

The following callbacks supported:

Each callback is class method of the model, it should accept single argument: `next`, this is callback which should be called after end of the hook. Except `afterInitialize` because this method is syncronous (called after `new Model`).

Object lifecycle

var user = new User;
// afterInitialize
user.save(callback);
// beforeValidation
// afterValidation
// beforeSave
// beforeCreate
// afterCreate
// afterSave
// callback
user.updateAttribute('email', 'email@example.com', callback);
// beforeValidation
// afterValidation
// beforeUpdate
// afterUpdate
// callback
user.destroy(callback);
// beforeDestroy
// afterDestroy
// callback
User.create(data, callback);
// beforeValidate
// afterValidate
// beforeCreate
// afterCreate
// callback

#afterInitialize(callback)

Call after initialize Model

User.afterInitialize = function (next) {
    // Pass control to the next
    next();
};

#beforeCreate(callback)

Call before create instance

User.beforeCreate = function (next) {
    // Pass control to the next
    next();
};

#afterCreate(callback)

Call after create instance

User.afterCreate = function (next) {
    // Pass control to the next
    next();
};

#beforeSave(callback)

Call before save instance

User.beforeSave = function (next) {
    // Pass control to the next
    next();
};

#afterSave(callback)

Call after save instance

User.afterSave = function (next) {
    // Pass control to the next
    next();
};

#beforeUpdate(callback)

Call before update instance

User.beforeUpdate = function (next) {
    // Pass control to the next
    next();
};

#afterUpdate(callback)

Call after update instance

User.afterUpdate = function (next) {
    // Pass control to the next
    next();
};

#beforeDestroy(callback)

Call before destroy instance

User.beforeDestroy = function (next) {
    // Pass control to the next
    next();
};

#afterDestroy(callback)

Call after destroy instance

User.afterDestroy = function (next) {
    // Pass control to the next
    next();
};

#beforeValidation(callback)

Call before validation instance

User.beforeValidation = function (next) {
    // Pass control to the next
    next();
};

#afterValidation(callback)

Call after validation instance

User.afterValidation = function (next) {
    // Pass control to the next
    next();
};