Entities on Node.js
/thanpolas/entity
Entities use
-
CIP
- for Classical Inheritance
- GH /thanpolas/cip
-
Bluebird
- for the 100% Promises API
- GH /petkaantonov/bluebird
-
Middlewarify
- for creating middleware
- GH /thanpolas/middlewarify
Entities extend
events.EventEmitter
...and that's the only thing they do
Creating an Entity
var entity = require('entity');
var EntityChild = entity.extend(function() {
this.a = 1;
});
var EntityGrandChild = EntityChild.extend();
entity.extend
var entity = require('entity');
var UserEntity = entity.extendSingleton(function() {});
/* ... */
var userEnt = UserEntity.getInstance();
entity.extendSingleton
Entities Adaptors
Mongoose
- MongoDB ORM
- http://mongoosejs.com/
Sequelize
- PostgreSQL
- MySQL
- MariaDB
- SQLite
- http://sequelizejs.com/
CRUD Primitives
create(data)
read(query=)
readOne(query)
readLimit(?query, offset, limit)
update(query, updateValues)
delete(query)
count(query=)
CRUD Primitives
create()
entity.create({name: 'thanasis'})
.then(function(udo) {
udo.name === 'thanasis'; // true
})
.catch(function(error) {
// deal with error.
});
... so on and so forth ...
Entity Hooks
Middlewarify in action
- before
- after
- last
Entity Hooks
before
// a middleware with synchronous resolution
entity.read.before(function(data){
if (!data.name) {
throw new TypeError('No go my friend');
}
});
// then...
entity.read({}).then(function(document) {
// you'll never get here
}, function(err) {
err instanceof Error; // true
err.message === 'No go my friend'; // true
});
Hooks are FiFo
Order MATTERS
Hooks are Middleware
Before Hooks
Get the exact same number or arguments
After & Last Hooks
Get the exact same number or arguments
plus the result
Entity Hooks
Asynchronicity
entity.create.before(function(data){
return promiseReturningFn(function(result) {
resolve(result + 1);
});
});
Extending Entities
adding new methods
Extending Entities
Just use the prototype
var Entity = require('entity');
var UserEntity = module.exports = Entity.extend();
UserEntity.prototype.report = function(userId) {
return promiseReturningAction(userId);
};
Extending Entities
Using method()
var Entity = require('entity');
var UserEntity = module.exports = Entity.extend(function() {
this.method('report', this._report.bind(this));
this.report.before(this._checkUserId.bind(this));
this.report.after(this._normalize.bind(this));
});
UserEntity.prototype._report = function(userId) {
return promiseReturningAction(userId);
};
Let's combine all up
var ClipEntity = module.exports = EntityBase.extendSingleton(function() {
this.setModel(clipModel.Model);
this.method('readOneApi', this.readOne);
this.method('readLimitApi', this.readLimit);
this.method('updateApi', this.update);
this.method('readApi', this.read);
// Apply system wide (global) filters
this.readLimitApi.before(this.systemFilter.bind(this));
this.readOneApi.before(this.systemFilter.bind(this));
// Clip Creation middleware
this.create.before(this._populateActiveEvent.bind(this));
this.create.after(this._processNewClip.bind(this));
// Record sanitization middleware
this.updateApi.after(helpers.skipArgs(this.sanitizeResult, 2, this));
this.readLimitApi.after(helpers.skipArgs(this.sanitizeResults, 3, this));
this.readOneApi.after(helpers.skipArgs(this.sanitizeResult, 1, this));
});
A Production-ish Entity
Entity Hands On
this.readLimitApi.before(this.systemFilter.bind(this));
/**
* Apply system filters in all incoming READ queries
* to exclude deleted and corrupt items.
*
* @param {Object} query The query.
*/
ClipEntity.prototype.systemFilter = function(query) {
query.notFound = { ne: true };
query.processed = true;
};
Entity Hands On
this.create.after(this._processNewClip.bind(this));
/**
* Post creation clip processing.
*
* @param {Object} data Item used to create the record.
* @param {app.entity.ClipProcess} processEnt The process entity.
* @param {mongoose.Document} clipItem The result.
* @return {Promise} A promise.
* @private
*/
ClipEntity.prototype._processNewClip = Promise.method(function(data, processEnt,
clipItem) {
processEnt.clipId = clipItem.id;
log.finest('_processNewClip() :: Clip Saved to DB, starting FS save...', clipItem.id);
return this._checkWatermarkAndStore(processEnt, clipItem)
.bind(processEnt)
.then(processEnt.createThumbnail)
.then(processEnt.checkS3)
.then(processEnt.updateDatabase)
.then(function() {
log.fine('_processNewClip() :: Clip processing finished:',
processEnt.sourceFilePath);
})
.catch(processEnt.moveToTrash.bind(processEnt))
.catch(processEnt._deleteRecord);
});
Entity Hands On
this.updateApi.after(helpers.skipArgs(this.sanitizeResult, 2, this));
You all know what happens here!
Now let's get crazy
Entities
+
Crude
- POST /user
- GET /user
- GET /user/:id
- PUT /user/:id
- PATCH /user/:id
- DELETE /user/:id
... Coming Soon
on the next νοδε.ξσ
Thank you
(here is where you applaud)
Thanasis Polychronakis
@thanpolas
Questions?
Thanasis Polychronakis
@thanpolas
Entities on Node.js
By thanpolas
Entities on Node.js
The Entities implementation for Node.js
- 873