Slide 1

Slide 1 text

Entities on Node.js Entities on Node.js

Slide 2

Slide 2 text

/thanpolas/entity /thanpolas/entity

Slide 3

Slide 3 text

Entities use Entities use CIP ​for Classical Inheritance /thanpolas/cip Bluebird for the 100% Promises API /petkaantonov/bluebird Middlewarify for creating middleware /thanpolas/middlewarify @thanpolas

Slide 4

Slide 4 text

Entities extend Entities extend events.EventEmitter ...and that's the only thing they do @thanpolas

Slide 5

Slide 5 text

Creating an Entity 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 @thanpolas

Slide 6

Slide 6 text

Entities Adaptors Entities Adaptors Mongoose Mongoose MongoDB ORM http://mongoosejs.com/ Sequelize Sequelize PostgreSQL MySQL MariaDB SQLite http://sequelizejs.com/ @thanpolas

Slide 7

Slide 7 text

CRUD Primitives CRUD Primitives create(data) read(query=) readOne(query) readLimit(?query, offset, limit) update(query, updateValues) delete(query) count(query=) @thanpolas

Slide 8

Slide 8 text

CRUD Primitives CRUD Primitives create() create() entity.create({name: 'thanasis'}) .then(function(udo) { udo.name === 'thanasis'; // true }) .catch(function(error) { // deal with error. }); ... so on and so forth ... @thanpolas

Slide 9

Slide 9 text

Entity Hooks Entity Hooks Middlewarify in action before after last @thanpolas

Slide 10

Slide 10 text

Entity Hooks Entity Hooks before 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 }); @thanpolas

Slide 11

Slide 11 text

Hooks are FiFo Hooks are FiFo Order MATTERS Order MATTERS @thanpolas

Slide 12

Slide 12 text

Hooks are Hooks are Middleware Middleware @thanpolas

Slide 13

Slide 13 text

Before Hooks Before Hooks Get the exact same number or arguments After & Last Hooks After & Last Hooks Gets the result plus the original number or arguments @thanpolas

Slide 14

Slide 14 text

Entity Hooks Entity Hooks Asynchronicity Asynchronicity entity.create.before(function(data){ return promiseReturningFn(function(result) { resolve(result + 1); }); }); @thanpolas

Slide 15

Slide 15 text

Extending Entities Extending Entities adding new methods adding new methods @thanpolas

Slide 16

Slide 16 text

Extending Entities Extending Entities Just use the prototype Just use the prototype var Entity = require('entity'); var UserEntity = module.exports = Entity.extend(); UserEntity.prototype.report = function(userId) { return promiseReturningAction(userId); }; @thanpolas

Slide 17

Slide 17 text

Extending Entities Extending Entities Using method() 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); }; @thanpolas

Slide 18

Slide 18 text

Let's combine all up Let's combine all up @thanpolas

Slide 19

Slide 19 text

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 A Production-ish Entity @thanpolas

Slide 20

Slide 20 text

Entity Hands On 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; }; @thanpolas

Slide 21

Slide 21 text

Entity Hands On 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) .catch(processEnt._deleteRecord); }); @thanpolas

Slide 22

Slide 22 text

Now let's get crazy Now let's get crazy @thanpolas

Slide 23

Slide 23 text

Entities Entities + + Crude Crude POST /user GET /user GET /user/:id PUT /user/:id PATCH /user/:id DELETE /user/:id /thanpolas/crude @thanpolas

Slide 24

Slide 24 text

... but not today ... but not today @thanpolas

Slide 25

Slide 25 text

Thank you (here is where you applaud) Thanasis Polychronakis @thanpolas speakerdeck.com/thanpolas

Slide 26

Slide 26 text

Questions? Be a critic Thanasis Polychronakis @thanpolas speakerdeck.com/thanpolas

Slide 27

Slide 27 text

Shameless Plug Time Promo Code bgwebsummit From 60€ --> 40€ 15/5/2015 @ Thessaloniki Greece devitconf.org