A pure JS client for interacting with server-side RESTful resources. Think Restangular without Angular.
It is available with bower or npm:
bower install restful.js
npm install restful.js
Include restful.min.js
to the HTML, and the restful
object is now available in the global scope:
<script type="text/javascript" src="/path/to/bower_components/restful.js/dist/restful.min.js"></script>
Alternately, you can use RequireJS or Browserify to avoid global scoping.
var restful = require('restful.js');
Start by defining the base endpoint for an API, for instance http://api.example.com
.
var api = restful('api.example.com');
You can add headers, port, custom protocol, or even a prefix (like a version number):
var api = restful('api.example.com')
.header('AuthToken', 'test') // set global header
.prefixUrl('v1')
.protocol('https')
.port(8080);
// resource now targets `https://api.example.com:8080/v1`
A collection is an API endpoint for a list of entities, for instance http://api.example.com/articles
. Create it using the all(name)
syntax:
var articlesCollection = api.all('articles'); // http://api.example.com/articles
articlesCollection
is just the description of the collection, the API wasn't fetched yet.
A member is an API endpoint for a single entity, for instance http://api.example.com/articles/1
. Create it using the one(name, id)
syntax:
var articleMember = api.one('articles', 1); // http://api.example.com/articles/1
Just like above, articleMember
is a description, not an entity.
You can chain one()
and all()
to target the required collection or member:
var articleMember = api.one('articles', 1); // http://api.example.com/articles/1
var commentsCollection = articleMember.all('comments'); // http://api.example.com/articles/1/comments
In case you need to set a custom endpoint URL, you can use oneUrl
or allUrl
methods.
var articleMember = api.oneUrl('articles', 'http://custom.url/article?id=1'); // http://custom.url/article?id=1
var articlesCollection = api.allUrl('articles', 'http://custom.url/article/list'); // http://custom.url/article/list
Once you have collections and members endpoints, fetch them to get entities. Restful.js exposes get()
and getAll()
methods for fetching endpoints. Since these methods are asynchronous, they return a Promise (based on the ES6 Promise specification) for response.
var articleMember = api.one('articles', 1); // http://api.example.com/articles/1
articleMember.get().then(function(response) {
var articleEntity = response.body();
var article = articleEntity.data();
console.log(article.title); // hello, world!
});
var commentsCollection = articleMember.all('comments'); // http://api.example.com/articles/1/comments
commentsCollection.getAll().then(function(response) {
var commentEntities = response.body();
commentEntities.forEach(function(commentEntity) {
var comment = commentEntity.data();
console.log(comment.body);
})
});
Tip: You can describe a member based on a collection and trigger the API fetch at the same time by calling get(id)
:
// fetch http://api.example.com/articles/1/comments/4
var articleMember = api.one('articles', 1);
var commentMember = articleMember.one('comments', 4);
commentMember.get().then(function(response) {
//
});
// equivalent to
var commentsCollection = articleMember.all('comments');
commentsCollection.get(4).then(function(response) {
//
});
A response is made from the HTTP response fetched from the endpoint. It exposes status()
, headers()
, and body()
methods. For a GET
request, the body
method will return one or a an array of entities. Therefore you can disable this hydration by calling body(false)
.
An entity is made from the HTTP response data fetched from the endpoint. It exposes a data()
method:
var articleCollection = api.all('articles'); // http://api.example.com/articles
// http://api.example.com/articles/1
api.one('articles', 1).get().then(function(response) {
var articleEntity = response.body();
// if the server response was { id: 1, title: 'test', body: 'hello' }
var article = articleEntity.data();
article.title; // returns `test`
article.body; // returns `hello`
// You can also edit it
article.title = 'test2';
// Finally you can easily update it or delete it
articleEntity.save(); // will perform a PUT request
articleEntity.remove(); // will perform a DELETE request
}, function(response) {
// The reponse code is not >= 200 and < 400
throw new Error('Invalid response');
});
You can also use the entity to continue exploring the API. Entities expose several other methods to chain calls:
entity.one(name, id)
: Query a member child of the entity.entity.all(name)
: Query a collection child of the entity.entity.url()
: Get the entity url.entity.save()
: Save the entity modifications by performing a POST request.entity.remove()
: Remove the entity by performing a DELETE request.entity.id()
: Get the id of the entity.
var articleMember = api.one('articles', 1); // http://api.example.com/articles/1
var commentMember = articleMember.one('comments', 3); // http://api.example.com/articles/1/comments/3
commentMember.get()
.then(function(response) {
var commentEntity = response.body();
// You can also call `all` and `one` on an entity
return comment.all('authors').getAll(); // http://api.example.com/articles/1/comments/3/authors
}).then(function(response) {
var authorEntities = response.body();
authorEntities.forEach(function(authorEntity) {
var author = authorEntity.data();
console.log(author.name);
});
});
Restful.js uses an inheritance pattern when collections or members are chained. That means that when you configure a collection or a member, it will configure all the collection an members chained afterwards.
// configure the api
api.header('AuthToken', 'test');
var articlesCollection = api.all('articles');
articlesCollection.get(1); // will send the `AuthToken` header
// You can configure articlesCollection, too
articlesCollection.header('foo', 'bar');
articlesCollection.one('comments', 1).get(); // will send both the AuthToken and foo headers
Restful.js exposes similar methods on collections, members and entities. The name are consistent, and the arguments depend on the context.
getAll ( [ params [, headers ]] )
: Get a full collection. Returns a promise with an array of entities.get ( id [, params [, headers ]] )
: Get a member in a collection. Returns a promise with an entity.post ( data [, headers ] )
: Create a member in a collection. Returns a promise with the response.put ( id, data [, headers ] )
: Update a member in a collection. Returns a promise with the response.delete ( id [, data, headers ] )
: Delete a member in a collection. Returns a promise with the response.patch ( id, data [, headers ] )
: Patch a member in a collection. Returns a promise with the response.head ( id, [, headers ] )
: Perform a HEAD request on a member in a collection. Returns a promise with the response.url ()
: Get the collection url.addResponseInterceptor ( interceptor )
: Add a response interceptor. You can only alter data and headers.addRequestInterceptor ( interceptor )
: Add a request interceptor.addFullResponseInterceptor ( interceptor )
: Add a full response interceptor. You can alter data and headers.addFullRequestInterceptor ( interceptor )
: Add a full request interceptor. You can alter params, headers, data, method and url.header ( name, value )
: Add a header.
// http://api.example.com/articles/1/comments/2/authors
var authorsCollection = api.one('articles', 1).one('comments', 2).all('authors');
authorsCollection.getAll().then(function(authorEntities) { /* */ });
authorsCollection.get(1).then(function(authorEntity) { /* */ });
get ( [ params [, headers ]] )
: Get a member. Returns a promise with an entity.put ( data [, headers ] )
: Update a member. Returns a promise with the response.delete ( [ data, headers ] )
: Delete a member. Returns a promise with the response.patch ( data [, headers ] )
: Patch a member. Returns a promise with the response.head ( [ headers ] )
: Perform a HEAD request on a member. Returns a promise with the response.one ( name, id )
: Target a child member in a collectionname
.all ( name )
: Target a child collectionname
.url ()
: Get the member url.addResponseInterceptor ( interceptor )
: Add a response interceptor.addRequestInterceptor ( interceptor )
: Add a request interceptor. You can only alter data and headers.addFullResponseInterceptor ( interceptor )
: Add a full response interceptor. You can alter data and headers.addFullRequestInterceptor ( interceptor )
: Add a full request interceptor. You can alter params, headers, data, method and url.header ( name, value )
: Add a header.
// http://api.example.com/articles/1/comments/2
var commentMember = api.one('articles', 1).one('comments', 2);
commentMember.get().then(function(commentEntity) { /* */ });
commentMember.delete().then(function(data) { /* */ });
A response or request interceptor is a callback which looks like this:
resource.addRequestInterceptor(function(data, headers, method, url) {
// to edit the headers, just edit the headers object
// You always must return the data object
return data;
});
A full request interceptor is a callback which looks like this:
resource.addFullRequestInterceptor(function(params, headers, data, method, url) {
//...
// all args had been modified
return {
params: params,
headers: headers,
data: data,
method: method,
url: url
};
// just return modified arguments
return {
headers: headers,
data: data
};
});
A full response interceptor is a callback which looks like this:
resource.addFullResponseInterceptor(function(data, headers, method, url) {
// all args had been modified (method and url is read only)
return {
headers: headers,
data: data
};
// just return modified arguments
return {
headers: headers
};
});
response.status()
: Get the HTTP status code of the responseresponse.headers()
: Get the HTTP headers of the responseresponse.body()
: Get the HTTP body of the response. If it is aGET
request, it will hydrate some entities. To get the raw body call it withfalse
as argument.
entity.data()
: Get the JS object unserialized from the response body (which must be in JSON)entity.one(name, id)
: Query a member child of the entity.entity.all(name)
: Query a collection child of the entity.entity.url()
: Get the entity url.entity.id()
: Get the id of the entity.entity.save ( [ headers ] )
: Update the member link to the entity. Returns a promise with the response.entity.remove ( [ headers ] )
: Delete the member link to the entity. Returns a promise with the response.
// http://api.example.com/articles/1/comments/2
var commentMember = api.one('articles', 1).one('comments', 2);
commentMember.get().then(function(commentEntity) {
commentEntity.save();
commentEntity.remove();
});
To deal with errors, you must use the catch
method on any of the returned promises:
var commentMember = resource.one('articles', 1).one('comments', 2);
commentMember
.get()
.then(function(commentEntity) { /* */ })
.catch(function(err) {
// deal with the error
});
Install dependencies:
make install
To rebuild the minified JavaScript you must run: make build
.
During development you can run make watch
to trigger a build at each change.
make test
All contributions are welcome and must pass the tests. If you add a new feature, please write tests for it.
This application is available under the MIT License, courtesy of marmelab.