RESTless is a lightweight data persistence library for Ember.js. (~3KB minified & gzipped)
It is used to communicate with a remote JSON API to map data between a backend and your Ember.js application. Eventually, the library can be extended to support various other data persistence layers.
One of its main goals is to reproduce much of the simple, useful features of ember-data, and reflect a similar API, while remaining lightweight and stable.
Ember-data is a great project and we look forward to it being production ready. RESTless does not contain all of the features provided by ember-data, but was created to be less complex and contain most of the functionality needed for basic CRUD apps. Transitioning between the two should be possible with minimal effort. The main departure from ember-data is the idea of the 'Store'. Instead, all transactions are executed immediately.
Include ember-restless.js from the dist/ folder in your application
RESTless can be referenced either with the namespace RESTless or the shorthand RL. Similar to Ember and Em.
Similar to defining the 'Store' using ember-data, instead define the REST 'Client' for your application. RESTless will automatically detect the RESTClient on your application namespace. Defining a client is currently optional, and only needed if you would like to provide a custom adapter.
App.RESTClient = RL.RESTClient.create({
revision: 1,
adapter: App.RESTAdapter
});
Create a custom adapter to set a url to your backend REST service and optionally the namespace. For example, if your REST API is located at http://api.myservice.com/v1
App.RESTAdapter = RL.RESTAdapter.create({
url: 'http://api.myservice.com',
namespace: 'v1'
});
See the Advanced section below for further use of a custom adapter.
Each model you create should extend RL.Model
App.User = RL.Model.extend();
Supported types are string, number, boolean, and date. With a custom adapter, you can register custom types and transformations.
App.User = RL.Model.extend({
name: RL.attr('string'),
isActive: RL.attr('boolean'),
followerCount: RL.attr('number'),
createdAt: RL.attr('date')
});
For one-to-one properties use the belongsTo attribute helper.
App.User = RL.Model.extend({
profile: RL.belongsTo('App.Profile')
});
App.Profile = RL.Model.extend({
user: RL.belongsTo('App.User')
});
One-to-many and many-to-many, use the hasMany helper.
For example, if a Post
model contains an array of Tag
models:
App.Post = RL.Model.extend({
tags: RL.hasMany('App.Tag')
});
App.Tag = RL.Model.extend({
name: RL.attr('string'),
count: RL.attr('number')
});
Currently, all relational data should be present in the expected json response. 'Sideloading' this data may be added in a future release. For example, all of the 'tags' data should be available in the initial response:
{
post: {
id: 1,
tags: [
{
id: 1,
name: 'tag1',
count: 50
},
{
id: 2,
name: 'tag2',
count: 11
}
]
}
}
To find a Post with an id of 1:
var post = App.Post.find(1);
To use a query to find:
var people = App.Person.find({ name: "Peter" });
Use findAll(), or alternatively use find() and omit parameters
var posts = App.Post.findAll();
or
var posts = App.Post.find();
Create records like you would a normal Ember Object
var post = App.Post.create({
title: 'My First Post',
body: 'Lorem ipsum...'
});
To save a record to the REST service call: saveRecord() RESTless will automatically POST to save a new record, or PUT to update and existing record
var post = App.Post.create();
post.saveRecord();
Updating:
post.set('title', 'My Very First Post');
post.saveRecord();
Will tell the REST service to delete the record, then destroy the object
post.deleteRecord();
RESTless supports most of the lifecycle states and events of ember-data. All model objects have the following properties added:
- isLoaded: Record(s) have been retrieved from REST service
- isDirty: The record has local changes that have not yet been saved remotely
- isSaving: Record is in the process of saving to the backend
- isNew: Record has been created but not yet saved remotely
- isError: Record has been attempted to be saved, updated, or deleted but the backend returned an error
Additionally, you can subscribe to events that are fired during the lifecycle
- didLoad
- didCreate
- didUpdate
- becameError
Event Examples:
var post = App.Post.create({
title: 'My First Post',
body: 'Lorem ipsum...'
});
post.on('didCreate', function() {
console.log('post created!');
});
post.on('becameError', function(error) {
console.log('error saving post!');
});
post.saveRecord();
var allPosts = App.Post.find();
allPosts.on('didLoad', function() {
console.log('posts retrieved!');
});
allPosts.on('becameError', function(error) {
console.log('error getting posts!');
});
You can use a custom adapter to set irregular plural resource names
App.RESTAdapter.configure("plurals", {
person: "people"
});
The primary key for all models defaults to 'id'. You can customize it per model class to match your backend:
App.RESTAdapter.map("App.Post", {
primaryKey: "slug"
});
For example, if your JSON has a key lastNameOfPerson
and the desired attribute name is lastName
:
App.Person = RL.Model.extend({
lastName: RL.attr('string')
});
App.RESTAdapter.map('App.Person', {
lastName: { key: 'lastNameOfPerson' }
});
Make attributes 'read-only', which will exclude them from being serialized into the json sent to your service when saving. For example, if you want to let the backend compute the date a record is created:
App.Person = RL.Model.extend({
firstName: RL.attr('string'),
lastName: RL.attr('string'),
createdAt: RL.attr('date', { readOnly: true })
});
If you want the entire model to be read-only and also remove all 'write' methods. Also provides a performance increase since each property won't have to be observed for 'isDirty'.
App.Post = RL.ReadOnlyModel.extend({
...
});
You can use a custom adapter to add custom transforms:
App.RESTAdapter.registerTransform('timeAgo', {
deserialize: function(serialized) {
// return a custom date string, such as: '5 minutes ago'
},
serialize: function(deserialized) {
// return a custom date json format for your backend or
// simply return deserialized
}
});
App.Comment = RL.Model.extend({
createdAt: RL.attr('timeAgo')
});
If you wish to build ember-restless youself, you will need node.js and Grunt.
- Install node: http://nodejs.org/
- Open a terminal window
- Install dependencies:
npm install
- Build:
grunt
- Output will be dist/ember-restless.js and dist/ember-restless.min.js
Uses QUnit.
To run tests, open tests/index.html in a browser.
Tests are currently a work in progress.
Coming soon.