js-data.io

 

@jmdobry

Me

Husband. Father.

Soccer. Lots of Soccer.

Canyoneering/Hiking.

 

Fun Facts

I speak Russian.

Completed Dota 2 all hero challenge.

My Daughter

Work

Software Engineer at Lendio.

Frontend + Node/PHP/MySql.

 

Data Science/Machine Learning (last 7 months).

Open Source

Talks

Side Project

codetrain.io - Write snippets of code. Execute. Share. Discuss.

Why js-data?

Background

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

~Web 2.0

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?

js-data philosophies

Easy, up and running fast

Recognizable API

Dead simple CRUD

Framework-agnostic

Storage-agnostic

Convention + Configuration

Design by Contract

Store + Adapters

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

Resources

var District = store.defineResource({
  name: 'district'
});

In-memory store

store.registerAdapter({
  name: 'school',
  relations: {
    belongsTo: {
      district: {
        localKey: 'district_id',
        localField: 'district'
      }
    }
  }
});

District

School

Defaults

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

Sync vs Async

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

Sync vs Async

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

How it works

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()

Demos

Features

Model Lifecycle Hooks

beforeCreate

afterDestroy

afterInject

beforeCreateInstance

etc.

Relations

belongsTo

hasMany

hasOne

loadRelations()
link(), linkAll(), unlink()

Features

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"

Features

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"

Features

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"
});

Features

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'
});

Thanks!

https://joind.in/14051

Made with Slides.com