persist is an ORM framework for node.js.
The following databases are currently supported:
- sqlite3 - via: node-sqlite3
- mysql - via: node-mysql
- PostgreSQL - via: node-postgres
- Oracle - via: node-oracle
var persist = require("persist");
var type = persist.type;
// define some model objects
Phone = persist.define("Phone", {
"number": type.STRING
});
Person = persist.define("Person", {
"name": type.STRING
}).hasMany(this.Phone);
persist.connect({
driver: 'sqlite3',
filename: 'test.db',
trace: true
}, function(err, connection) {
Person.using(connection).all(function(err, people) {
// people contains all the people
});
});
You can install using Node Package Manager (npm):
npm install persist
- hasMany
- hasOne
- using
- save
- update (instance)
- update
- delete
- getById
- defineClause
- onSave
- onLoad
- validate
- Associated Object Properties
- Model events
If the current working directory contains a file called database.json this file will be loaded upon requiring persist. The file should follow a format like this:
{
"default": "dev",
"dev": {
"driver": "sqlite3",
"filename": ":memory:"
},
"test": {
"driver": "sqlite3",
"filename": ":memory:"
},
"prod": {
"driver": "sqlite3",
"filename": "prod.db",
"sqlDir": "./prodSql",
"pooling": {
"name": "testPool",
"max": 2,
"min": 1,
"idleTimeoutMillis": 30000
}
}
}
"default" specifies which environment to load.
## persist ### persist.envThe environment to read from the database.json file. If not set will use the value of default from the database.json.
Example
persist.env = 'prod';
Connects to a database.
Arguments
- options - (optional) Options used to connect to the database. If options are not specified the default connect options are used. see database.json and SetDefaultConnectOptions
- driver - The driver to use to connect (ie sqlite3, mysql, oracle, or postgresql).
- db - If db is specified this parameter will be assumed to be an already open connection to the database.
- other - see the documentation for your driver. The options hash will be passed to that driver.
- callback(err, connection) - Callback to be called when the connection is established.
Example
persist.connect({
driver: 'sqlite3',
filename: 'test.db',
trace: true
}, function(err, connection) {
// connnection esablished
});
Defines a model object for use in persist.
The primary key column does not need to be specified and will default to the name 'id' with the attributes dbColumnName='id', type='integer'. You can override the database name using dbColumnName or setting the primaryKey attribute on any column.
Arguments
- modelName - The name of the model. This name will map to the database name.
- properties - Hash of properties (or columns). The value of each property can simply be the type name (ie type.STRING) or it can be a hash of more options.
- type - type of the property (ie type.STRING)
- defaultValue - this can be a value or a function that will be called each time this model object is created
- dbColumnName - the name of the database column. (default: name of the property, all lower case, seperated by '_')
- primaryKey - Marks this column as being the primary key column. You can have only one primary key column.
- opts - Options for this column.
- tableName - The name of the table (default: modelName pluralized).
Returns
A model class. Events can also be registered with model instances - see Model Events
Example
Person = persist.define("Person", {
"name": type.STRING,
"createdDate": { type: type.DATETIME, defaultValue: function() { return self.testDate1 }, dbColumnName: 'new_date' },
"lastUpdated": { type: type.DATETIME }
})
Defines a model object for use in persist. Columns are defined by the program in this method. Uses an existing database connection to retrieve column data.
Arguments
- modelName - The name of the model. This name will map to the table name.
- dbConfig - Hash of dbConfig. Should contain the driver, as well as the database name.
- database - The database connection to use.
- driver - The name of the database driver to use.
Returns
A model class. Events can also be registered with model instances - see Model Events
Example
persist.defineAuto("Person",{driver:dbDriver, db:self.connection.db},function(err,model){
Person = model.hasMany(Phone)
.on('beforeSave', function (obj) {
obj.lastUpdated = testDate;
})
.on('afterSave', function (obj) {
if (!obj.updateCount) obj.updateCount = 0;
obj.updateCount++;
});
});
Sets the default connection options to be used on future connect calls. see database.json
Arguments
- options - See connect for the description of options
Example
persist.setDefaultConnectOptions({
driver: 'sqlite3',
filename: 'test.db',
trace: true});
Shutdown persist. This is currently only required if you are using connection pooling. see generic-pool.
Arguments
- [callback] - Optional callback on successful shutdown.
Example
persist.shutdown(function() {
console.log('persist shutdown');
});
Chains multiple statements together in order and gets the results.
Arguments
- chainables - An array of chainable queries. These can be save, updates, selects, or deletes. Each item in the array will be executed, wait for the results, and then execute the next. This can also be a hash of queries in which case the results will contain a hash of results where each key corresponds to a key in the results.
- callback(err, results) - Callback called when all the items have been executed.
Example
// array chaining
connection.chain([
person3.save,
Person.min('age'),
Person.max('age'),
phone3.delete,
person2.delete,
Person.orderBy('name').all,
Phone.orderBy('number').first,
Phone.count,
Phone.deleteAll,
Phone.all,
Person.getById(1),
persist.runSql('SELECT * FROM Person')
], function(err, results) {
// results[0] = person3
// results[1] = 21
// results[2] = 25
// results[3] = []
// results[4] = []
// results[5] = -- all people ordered by name
// results[6] = -- first phone ordered by number
// results[7] = 100
// results[8] = []
// results[9] = [] -- nobody left
// results[10] = -- the person with id 1
// results[11] = Results of select.
});
// mapped chaining
connection.chain({
minAge: Person.min('age'),
maxAge: Person.max('age')
}, function(err, results) {
// results.minAge = 21
// results.maxAge = 25
});
Begins a transaction on the connection.
Arguments
- callback(err, tx) - Callback called when the transaction has started. tx is a transaction object which you can call commit or rollback
Example
connection.tx(function(err, tx) {
person1.save(connection, function(err) {
tx.commit(function(err) {
// person1 saved and committed to database
});
});
});
Runs a sql statement that does not return results (INSERT, UPDATE, etc).
Arguments
- sql - The SQL statement to run.
- values - The values to substitute in the SQL statement. This is DB specific but typically you would use "?".
- callback(err, results) - Callback called when SQL statement completes. results will contain the number of affected rows or last insert id.
Example
connection.runSql("UPDATE people SET age = ?", [32], function(err, results) {
// people updated
});
Runs a sql statement that returns results (ie SELECT).
Arguments
- sql - The SQL statement to run.
- values - The values to substitute in the SQL statement. This is DB specific but typically you would use "?".
- callback(err, results) - Callback called when SQL statement completes. results will contain the row data.
Example
connection.runSqlAll("SELECT * FROM people WHERE age = ?", [32], function(err, people) {
// people contains all the people with age 32
});
Runs a sql statement that returns results (ie SELECT). This is different from runSqlAll in that it returns each row in a seperate callback.
Arguments
- sql - The SQL statement to run.
- values - The values to substitute in the SQL statement. This is DB specific but typically you would use "?".
- callback(err, row) - Callback called for each row returned.
- doneCallback(err) - Callback called after all the rows have returned.
Example
connection.runSqlEach("SELECT * FROM people WHERE age = ?", [32], function(err, person) {
// a single person
}, function(err) {
// all done
});
Same as runSql, runSqlAll, runSqlEach except the first parameter is a filename of where to load the SQL from.
Example
connection.runSqlFromFile('report.sql', [32], function(err, person) {
// a single person
}, function(err) {
// all done
});
Adds a has many relationship to a model. This will automatically add a property to the associated model which links to this model. It will also define a property on instances of this model to get the releated objects - see Associated Object Properties
Arguments
- AssociatedModel - The name of the model to associate to.
- options - (optional) An hash of options.
- through - creates a many to many relationship using the value of through as the join table.
- name - the name of the property to expose.
Returns
The model class object suitable for chaining.
Example
Phone = persist.define("Phone", {
"number": persist.String
});
Person = persist.define("Person", {
"name": persist.String
}).hasMany(Phone);
Adds a has one relationship to a model. This will automatically add a property to the associated model which links to this model. It will also define a property on instances of this model to get the releated objects - see Associated Object Properties
Arguments
- AssociatedModel - The name of the model to associate to.
- options - (optional) An hash of options.
- foreignKey - The foreign key to use for the relationship
- name - the name of the property to expose.
- createHasMany - true/false to create the other side of the relationship.
- hasManyName - The name of the property on the other side of the relationship.
Returns
The model class object suitable for chaining.
Example
Phone = persist.define("Phone", {
"number": persist.String
}).hasOne(Person);
Person = persist.define("Person", {
"name": persist.String
});
Gets a query object bound to a connection object.
Arguments
- connection - The connection to bind the query object to.
Returns
A new Query object.
Example
Person.using(connection).first(...);
Saves the model object to the database
Arguments
- connection - The connection to use to save the object with.
- callback(err) - The callback to be called when the save is complete
Example
person1.save(connection, function() {
// person1 saved
});
Updates the model object to the database
Arguments
- connection - The connection to use to update the object with.
- params - Object containing properties to update.
- callback(err) - The callback to be called when the update is complete
Example
person1.update(connection, { name: 'Tom' }, function() {
// person1 saved
});
Updates the model object specified with id to the database. This will only update the values specified and will not retreive the item from the database first.
Arguments
- connection - The connection to use to update the object with.
- id - The id of the row you would like to update.
- params - Object containing properties to update.
- callback(err) - The callback to be called when the update is complete
Example
Person.update(connection, 5, { name: 'Tom' }, function() {
// person with id = 5 updated with name 'Tom'.
});
// or chaining
connection.chain([
Person.update(5, { name: 'Tom' })
], function(err, results) {
// person with id = 5 updated with name 'Tom'.
});
Deletes the model object from the database
Arguments
- connection - The connection to use to delete the object with.
- callback(err) - The callback to be called when the delete is complete
Example
person1.delete(connection, function() {
// person1 deleted
});
Gets an object from the database by id.
Arguments
- connection - The connection to use to delete the object with.
- id - The if of the item to get.
- callback(err, obj) - The callback to be called when the delete is complete
Example
Person.getById(connection, 1, function(err, person) {
// person is the person with id equal to 1. Or null if not found
});
Creates a custom method that is a composition of clauses. this
is set to refer to the query.
you're constructing.
Arguments
- clauseName - The name of the clause to be attached to the model
- clauses - The function that describes the clause composition using a query.
Example
Person.defineClause('clauseName', function(arg1, arg2, ...) {
return this.where('id < ?', arg1).orderBy('id').limit(5);
});
Person.clauseName(5).all(connection, function(err, people) {
// All the people with id < 5, ordered by id and limited to 5
});
Person.defineClause('clauseName2', function(connection, callback) {
return this
.where('id < 5')
.orderBy('id')
.limit(5)
.all(connection, callback);
});
Person.clauseName2(connection, function(err, people) {
// All the people with id < 5, ordered by id and limited to 5
});
If preset this function will be called when an update or save occures. You would typically create this method in your model file.
Arguments
- obj - The object or partial object, in the case of update, being saved.
- connection - The connection persist is currently using to do the save
- callback() - The callback to be called when the onSave is complete
Example
Person.onSave = function(obj, connection, callback) {
obj.lastUpdated = new Date();
callback();
};
If preset this function will be called after an object is loaded from the database. You would typically create this method in your model file.
Arguments
- obj - The object that was just loaded from the database.
- connection - The connection persist is currently using to do the save
- callback() - The callback to be called when the onLoad is complete
Example
Person.onLoad = function(obj, connection, callback) {
obj.fullName = obj.firstName + ' ' + obj.lastName;
callback();
};
Model validation is loosely implemented. Instead, it's left to the developers to integrate any valiation library that fits their needs. If present this function will be called during a save or update operation.
Arguments
- obj - The model object
- connection - The connection persist is currently using to do the save or update
- callback(success, message) - The callback to be called when the validate is complete.
- success - False if validation failed, True otherwise
- message - A string containing an error message, or a custom-defined object containing vaidation information
Example
Person = persist.define("Person", {
"name": type.STRING,
"age": type.INTEGER
};
//Single message validation
Person.validate = function (obj, connection, callback) {
if (obj.name === 'bad name') {
return callback(false, 'You had a bad name');
}
return callback(true);
};
//Multiple message validation
Person.validate = function (obj, connection, callback) {
var errors = [];
if (obj.name === 'bad name') {
errors.push({name:'You had a bad name'});
}
if (obj.age < 0) {
errors.push({age:'Age must be greater than 0'});
}
if(errors.length > 0) {
return callback(false, errors);
}
return callback(true);
};
If you have setup an associated property using hasMany instances of your model will have an additional property which allows you to get the associated data. This property returns a Query object which you can further chain to limit the results.
Example
Phone = persist.define("Phone", {
"number": persist.String
});
Person = persist.define("Person", {
"name": persist.String
}).hasMany(Phone);
Person.using(connection).first(function(err, person) {
person.phones.orderBy('number').all(function(err, phones) {
// all the phones of the first person
});
});
The following events can be registered when defining a new model:
beforeCreate
andafterCreate
: fired before/after a new object is being added to the DBbeforeUpdate
andafterUpdate
: fired before/after an existing object is being updated in the DBbeforeSave
andafterSave
: fired before/after an object is either created or updated.beforeDelete
andafterDelete
: fired before/after an object is removed from the DB
Each event function has the signature: function(obj) where 'obj' is the model instance that fired the event.
Limitation
Events are currently fired ONLY when invoking save/update/delete functions on model instances. So for instance, Model.update(connection,id,data) will not fire the save & update events.
Example
Phone = persist.define("Phone", {
"number": persist.String,
"created_at": persist.DATETIME,
"updated_at": persist.DATETIME
})
.on("beforeCreate", function(obj){
obj.created_at = new Date();
})
.on("beforeSave", function(obj){
// updated when creating or updating the model instance
obj.updated_at = new Date();
});
Gets all items from a query as a single array of items. The array returned will have additional methods see here for documentation.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- callback(err, items) - Callback to be called after the rows have been fetched. items is an array of model instances.
Example
Person.all(connection, function(err, people) {
// all the people
});
Gets items from a query calling the callback for each item returned.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- callback(err, item) - Callback to be called after each row has been fetched. item is a model instance.
- doneCallback(err) - Callback called after all rows have been retrieved.
Example
Person.each(connection, function(err, person) {
// a person
}, function() {
// all done
});
Gets the first item from a query.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- callback(err, item) - Callback to be called after the row has been fetched. item is a model instance.
Example
Person.first(connection, function(err, person) {
// gets the first person
});
Gets the last item from a query.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- callback(err, item) - Callback to be called after the row has been fetched. item is a model instance.
Example
Person.last(connection, function(err, person) {
// gets the last person
});
Orders the results of a query.
Arguments
- propertyName - Name of the property to order by.
- direction - The direction to orderBy. Can be persist.Ascending or persist.Descending.
Returns
The query object suitable for chaining.
Example
Person.orderBy('name').all(connection, function(err, people) {
// all the people ordered by name
});
Limits the number of results of a query.
Arguments
- count - Number of items to return.
- offset - (Optional) The number of items to skip.
Returns
The query object suitable for chaining.
Example
Person.orderBy('name').limit(5, 5).all(connection, function(err, people) {
// The 5-10 people ordered by name
});
Filters the results by a where clause.
Arguments
- clause - A clause to filter the results by.
- values - (Optional) A single value or array of values to substitute in for '?'s in the clause.
- hash - A hash of columns and values to match on (see example)
Returns
The query object suitable for chaining.
Example
Person.where('name = ?', 'bob').all(connection, function(err, people) {
// All the people named 'bob'
});
Person.where('name = ? or age = ?', ['bob', 23]).all(connection, function(err, people) {
// All the people named 'bob' or people with age 23
});
Person.where({'name': 'bob', 'age': 23}).all(connection, function(err, people) {
// All the people named 'bob' with the age of 23
});
Arguments
- property - The property to invoke the IN clause on.
- values - An array of values to include in the IN clause.
Returns
The query object suitable for chaining.
Example
Person.whereIn('name', ['bob', 'alice', 'cindy']).all(connection, function(err,people) {
// All the people named 'bob', 'alice', or 'cindy'
});
Person.include("phones").whereIn('phones.number', ['111-2222','333-4444']).all(connection, function(err,people){
// All the people whose phone numbers are '111-2222' or '333-4444'
});
Counts the number of items that would be returned by the query.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- callback(err, count) - Callback with the count of items.
Example
Person.where('name = ?', 'bob').count(connection, function(err, count) {
// count = the number of people with the name bob
});
Gets the minimum value in the query of the given field.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- fieldName - The field name of the value you would like to get the minimum for.
- callback(err, min) - Callback with the minimum value.
Example
Person.where('name = ?', 'bob').min(connection, 'age', function(err, min) {
// the minimum age of all bobs
});
Gets the maximum value in the query of the given field.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- fieldName - The field name of the value you would like to get the maximum for.
- callback(err, min) - Callback with the maximum value.
Example
Person.where('name = ?', 'bob').max(connection, 'age', function(err, min) {
// the maximum age of all bobs
});
Gets the sum of all values in the query of the given field.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- fieldName - The field name you would like to sum.
- callback(err, sum) - Callback with the sum value.
Example
Person.where('name = ?', 'bob').sum(connection, 'age', function(err, sum) {
// the sum of all ages whos name is bob
});
Deletes all the items specified by the query.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- callback(err) - Callback called upon completion.
Example
Person.where('name = ?', 'bob').deleteAll(connection, function(err) {
// all people name 'bob' have been deleted.
});
Updates all the items specified by the query.
Arguments
- connection - (Optional) The connection to use. If this is not specified a using statement must be specified earlier.
- data - A hash of properties to update. Key is the property name to update. Value is the value to update the property to.
- callback(err) - Callback called upon completion.
Example
Person.where('name = ?', 'bob').updateAll(connection, { age: 25 }, function(err) {
// all people name 'bob' have their age set to 25.
});
Includes the associated data linked by (hasMany or hasMany(through)) the propertyName when retrieving data from the database. This will replace obj.propertyName with an array of results as opposed to the default before which is a query.
Internally this will do a join to the associated table in the case of a one to many. And will do a join to the associated through table and the associated table in the case of a many to many.
Arguments
- propertyName - This can be a single property name or an array of property names to include.
Example
Person.include("phones").where('name = ?', 'bob').all(connection, function(err, people) {
// all people named 'bob' and all their phone numbers
// so you can do... people[0].phones[0].number
// as opposed to... people[0].phones.all(function(err, phones) {});
});
Commits a transaction.
Arguments
- callback(err) - Callback called when the transaction has committed.
Example
connection.tx(function(err, tx) {
person1.save(connection, function(err) {
tx.commit(function(err) {
// person1 saved and committed to database
});
});
});
Rollsback a transaction.
Arguments
- callback(err) - Callback called when the transaction has rolledback.
Example
connection.tx(function(err, tx) {
person1.save(connection, function(err) {
tx.rollback(function(err) {
// person1 not saved. Transaction rolledback.
});
});
});
Gets an item from the result set by id.
Arguments
- id - The id of the item to get.
Example
Person.all(connection, function(err, people) {
var person2 = people.getById(2);
});
Persist uses generic-pool to manage the connection pool. If you specify "pooling" in your configuration you must specify a pool name. See generic-pool for other options. To cleanly shutdown the connection pool you must also call persist.shutdown.
Example database.json to enable pooling:
{
"default": "dev",
"dev": {
"driver": "sqlite3",
"filename": ":memory:",
"pooling": {
"name": "myDatabasePool"
}
}
}