Getting Started
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
Connecting to DB
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
- default value:
-
string user:
- default value:
process.env.USER
- Database user
- default value:
-
string database:
- default value:
process.env.USER
- database to use when connecting to database server
- default value:
-
string password:
- default value:
null
- user's password for database server
- default value:
-
number port:
- default value:
depends by adapter
- port to use when connecting to database server
- default value:
-
string host:
- default value:
null
- host address of database server
- used to initialize underlying net.Stream()
- default value:
-
bool pool:
- default value:
false
- used pooling connections to connect to server
- default value:
-
bool ssl:
- default value:
false
- whether to try SSL/TLS to connect to server
- default value:
-
string driver:
Schema usage
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 });
Common API methods
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 });
Query Interface
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 });
Middleware (Hooks)
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(); };