Jason Dobry
Husband. Father. Software Engineer. Open Source hacker. Always learning.
@jmdobry
Husband. Father.
Soccer. Lots of Soccer.
Canyoneering/Hiking.
I speak Russian.
Completed Dota 2 all hero challenge.
Software Engineer at Lendio.
Frontend + Node/PHP/MySql.
Data Science/Machine Learning (last 7 months).
codetrain.io - Write snippets of code. Execute. Share. Discuss.
Server-side rendering
Browser
Server
Request
Html
Majority of the work was done on the server
Browsers were slow
Example: Java Struts + .jsp
The only storage option
was your database
Controller
Model
View
View
User
DB
Browsers got faster
Browser
Server
REST
JSON
Offload work to the clients
Demand for dynamic content
More storage options: localStorage, indexedDB, BAAS
Send data & templates to clients, render in the browser
Controller
Model
View
User
Request
Model
Controller
localstorage
indexdDB
BAAS
DB
Server
REST
JSON
Model = ?
Request
jQuery
MooTools
Mithril
Ember
Angular
React
ExtJS
Backbone
Dojo
Knockout
Spine
Polymer
YUI
CanJS
Ampersand
Vue.js
Aurelia
Meteor
Flight
DB
js-data server adapters:
MySql
PostgreSql
MariaDB
SqlLite3
Redis
MongoDB
RethinkDB
HTTP
Firebase
Browser
Model = ?
js-data
js-data
So many...
localStorage
indexedDB
BAAS
js-data client adapters:
HTTP
Firebase
localStorage
localForage
You?
Easy, up and running fast
Recognizable API
Dead simple CRUD
Framework-agnostic
Storage-agnostic
Convention + Configuration
Design by Contract
var store = new JSData.DS();
In-memory store
store.registerAdapter(
'http',
new DSHttpAdapter(),
{ default: true }
);
var basePath = 'https://js-data-firebase.firebaseio.com';
store.registerAdapter(
'fb',
new DSFirebaseAdapter({
basePath: basePath
})
);
http
firebase
var District = store.defineResource({
name: 'district'
});
In-memory store
store.registerAdapter({
name: 'school',
relations: {
belongsTo: {
district: {
localKey: 'district_id',
localField: 'district'
}
}
}
});
District
School
In-memory store
District
School
idAttribute:"id"
GET /district
POST /district
GET /district/:id
PUT /district/:id
DELETE /district/:id
idAttribute:"id"
GET /school
POST /school
GET /school/:id
PUT /school/:id
DELETE /school/:id
"belongsTo" -> District
default settings
inherit
District.get(1); // undefined
store
http
DS#get
undefined
var promise = District.find(1);
DS#find
Promise
GET /district/1
JSON
DS#get
{ id: 1, ... }
promise.then(function (district) {
district; // { id: 1, name: 'Alpine' }
District.get(1) === district; // true
return District.find(1);
}).then(function (district) {
District.get(1) === district; // true
});
resolve
DS#find
resolve
var district = District.get(1);
district.name = 'Wasatch';
store
http
DS#get
{ id: 1, ... }
var promise = District.save(1);
DS#save
Promise
PUT /district/1
JSON
DS#get
{ id: 1, ... }
promise.then(function (district) {
district; // { id: 1, name: 'Wasatch' }
District.get(1) === district; // true
return District.destroy(1);
}).then(function () {
District.get(1); // undefined
});
resolve
DS#destroy
resolve
DELETE /district/1
response
Sync (in-memory store)
Async (delegated to adapters)
get()
find(), loadRelations()
filter()
findAll(), loadRelations()
inject(), item.foo = 'bar'
save(), update(), updateAll()
eject(), ejectAll()
destroy(), destroyAll()
Model Lifecycle Hooks
beforeCreate
afterDestroy
afterInject
beforeCreateInstance
etc.
Relations
belongsTo
hasMany
hasOne
loadRelations()
link(), linkAll(), unlink()
Static Methods
Instance Methods
var User = store.DefineResource({
name: 'user',
actions: {
fooStaticMethod: {
method: 'POST'
}
}
});
User.barStaticMethod = function () {...};
User.fooStaticMethod();
User.barStaticMethod();
var User = store.DefineResource({
name: 'user',
methods: {
say: function () {
return this.name;
}
}
});
var user = User.createInstance({
name: 'John'
});
user.say(); // "John"
Convenient Shorthands
Computed Properties
var User = store.DefineResource('user'});
var user = User.inject({ id: 1 });
user.name = 'John';
// these are equivalent
store.save('user', 1);
User.save(1);
user.DSSave();
var User = store.DefineResource({
name: 'user',
computed: {
fullName: ['first', 'last', function () {
return this.first + ' ' + this.last;
]
}
});
var user = User.inject({
id: 1,
first: 'John',
last: 'Anderson'
});
user.fullName; // "John Anderson"
Query Collections
Schemas + Validation
var User = store.DefineResource('user'});
User.inject({ id: 1, age: 30 });
User.inject({ id: 2, age: 15 });
User.inject({ id: 3, age: 15 });
User.filter({
where: {
age: {
'>': 18
}
}
}); // [{ id: 1, age: 30 }]
User.filter({
age: 15
}); // [{id: 1,age: 15},{id: 3,age: 15}]
// Requires js-data-schema
var User = store.DefineResource({
name: 'user',
schema: {
name: 'string'
}
});
var user = User.inject({
id: 1,
name: 999
});
user.DSSave().catch(function (err) {
err.name.errors[0].rule; // "type"
err.name.errors[0].expected; // "string"
err.name.errors[0].actual; // "number"
});
Change Detection
Cache Expiration
// uses Object.observe (or polyfill)
var User = store.DefineResource('user'});
var user = User.inject({ id: 1 });
User.hasChanges(1); // false
user.name = 'John';
setTimeout(function () {
User.hasChanges(1); // true
User.previous(1); // { id: 1 }
User.changes(1); // {added:{name:'John}}
}, 30);
var User = store.DefineResource({
name: 'user',
// check every 30 sec
reapInterval: 30000,
// items expire after 15 min
maxAge: 900000,
// eject expired items
reapAction: 'eject'
});
https://joind.in/14051
By Jason Dobry
Give your data the treatment it deserves with a framework-agnostic data store built for ease of use and peace of mind. Works in NodeJS and in the Browser.
Husband. Father. Software Engineer. Open Source hacker. Always learning.