restangular

AngularJS service to handle Rest API Restful Resources properly and easily
Homepage:
102 people use it
Author: 476f8170f26e7d20b6fee1714f0a9969?d=https%3a%2f%2fidenticons.github.com%2f48f5c66df135f4dad0e2ee6b8acede59 mgonto
Submitted by: Assets.github.com%2fimages%2fgravatars%2fgravatar-user-420 ottsch

IMPORTANT NOTE

Code isn't visible in here. To see this README complete with code go to https://github.com/mgonto/restangular/blob/master/README.md

Restangular

Build Status

Restangular is an AngularJS service that simplifies common GET, DELETE, and UPDATE requests with a minimum of client code. It's a perfect fit for any WebApp that consumes data from a RESTful API.

Check out a, live demo on plunkr. It uses the same example as the official Angular Javascript Project... but Restangularized!

Table of contents

Differences with $resource

Restangular has several features that distinguish it from $resource:

  • It uses promises. Instead of doing the "magic" filling of objects like $resource, it uses promises.
  • You can use this in $routeProvider.resolve. As Restangular returns promises, you can return any of the methods in the $routeProvider.resolve and you'll get the real object injected into your controller if you want.
  • It doesn't have all those $resource bugs. Restangular doesn't have problem with trailling slashes, additional : in the URL, escaping information, expecting only arrays for getting lists, etc.
  • It supports all HTTP methods.
  • It supports ETag out of the box. You don't have to do anything. ETags and If-None-Match will be used in all of your requests
  • It supports self linking elements If you receive from the server some item that has a link to itself, you can use that to query the server instead of writing the URL manually.
  • You don't have to create one $resource object per request. Each time you want to do a request, you can just do it using the object that was returned by Restangular. You don't need to create a new object for this.
  • You don't have to write or remember ANY URL. With $resource, you need to write the URL Template. In here, you don't write any urls. You just write the name of the resource you want to fetch and that's it.
  • It supports nested RestFUL resources. If you have Nested RestFUL resources, Restangular can handle them for you. You don't have to know the URL, the path, or anything to do all of the HTTP operations you want.
  • Restangular lets you create your own methods. You can create your own methods to run the operation that you want. The sky is the limit.
  • Support for wrapped responses. If your response for a list of element actually returns an object with some property inside which has the list, it's very hard to use $resource. Restangular knows that and it makes it easy on you. Check out https://github.com/mgonto/restangular#my-response-is-actually-wrapped-with-some-metadata-how-do-i-get-the-data-in-that-case
  • You can build your own URLs with Restangular objects easily. Restangular lets you create a Restangular object for any url you want with a really nice builder.

Let's see a quick and short example of these features ````javascript // Restangular returns promises Restangular.one('users').getList() // GET: /users .then(function(users) { // returns a list of users $scope.user = users[0]; // first Restangular obj in list: { id: 123 } })

// Later in the code...

// Restangular objects are self-aware and know how to make their own restful requests $scope.user.getList('cars'); // GET: /users/123/cars

// You can also use your own custom methods on Restangular objects $scope.user.sendMessage(); // POST: /users/123/sendMessage

// Chain methods together to easily build complex requests $scope.user.one('messages', 123).one('from', 123).getList('unread'); // GET: /user/123/messages/123/from/123/unread

Dependencies

Restangular depends on Angular and Lodash (or Underscore). angular-resource is no longer needed since version 1.0.6, now this uses $http instead of $resource

Starter Guide

Quick Configuration (For Lazy Readers)

This is all you need to start using all the basic Restangular features.

The Restangular service may be injected into any Controller or Directive :)

Using Restangular

Creating Main Restangular object

There are 3 ways of creating a main Restangular object. The first one and most common one is by stating the main route of all requests. The second one is by stating the main route and object of all requests.

Let's code!

Now that we have our main Object let's start playing with it.

Configuring Restangular

Properties

Restangular comes with defaults for all of its properties but you can configure them. So, if you don't need to configure something, there's no need to add the configuration. You can set all these configurations in RestangularProvider or Restangular service to change the global configuration or you can use the withConfig method in Restangular service to create a new Restangular service with some scoped configuration. Check the section on this later.

setBaseUrl

The base URL for all calls to your API. For example if your URL for fetching accounts is http://example.com/api/v1/accounts, then your baseUrl is /api/v1. The default baseUrl is an empty string which resolves to the same url that AngularJS is running, so you can also set an absolute url like http://api.example.com/api/v1 if you need do set another domain.

setExtraFields

These are the fields that you want to save from your parent resources if you need to display them. By default this is an Empty Array which will suit most cases

setParentless

Use this property to control whether Restangularized elements to have a parent or not. So, for example if you get an account and then get a nested list of buildings, you may want the buildings URL to be simple /buildings/123 instead of /accounts/123/buildings/123. This property lets you do that.

This method accepts 2 parameters:

  • Boolean: Specifies if all elements should be parentless or not
  • Array: Specifies the routes (types) of all elements that should be parentless. For example ['buildings']

setDefaultHttpFields

$http from AngularJS can receive a bunch of parameters like cache, transformRequest and so on. You can set all of those properties in the object sent on this setter so that they will be used in EVERY API call made by Restangular. This is very useful for caching for example. All properties that can be set can be checked here: http://docs.angularjs.org/api/ng.$http#parameters

addElementTransformer

This is a hook. After each element has been "restangularized" (Added the new methods from Restangular), the corresponding transformer will be called if it fits.

This should be used to add your own methods / functions to entities of certain types.

You can add as many element transformers as you want. The signature of this method can be one of the following:

  • addElementTransformer(route, transformer): Transformer is called with all elements that have been restangularized, no matter if they're collections or not.

  • addElementTransformer(route, isCollection, transformer): Transformer is called with all elements that have been restangularized and match the specification regarding if it's a collection or not (true | false)

setOnElemRestangularized

This is a hook. After each element has been "restangularized" (Added the new methods from Restangular), this will be called. It means that if you receive a list of objects in one call, this method will be called first for the collection and then for each element of the collection.

I favor the usage of addElementTransformer instead of onElemRestangularized whenever possible as the implementation is much cleaner.

This callback is a function that has 3 parameters:

  • elem: The element that has just been restangularized. Can be a collection or a single element.
  • isCollection: Boolean indicating if this is a collection or a single element.
  • what: The model that is being modified. This is the "path" of this resource. For example buildings
  • Restangular: The instanced service to use any of its methods

This can be used together with addRestangularMethod (Explained later) to add custom methods to an element

setResponseInterceptor (or setResponseExtractor. It's an Alias)

The responseInterceptor is called after we get each response from the server. It's a function that receives this arguments:

  • data: The data received got from the server
  • operation: The operation made. It'll be the HTTP method used except for a GET which returns a list of element which will return getList so that you can distinguish them.
  • what: The model that's being requested. It can be for example: accounts, buildings, etc.
  • url: The relative URL being requested. For example: /api/v1/accounts/123
  • response: Full server response including headers
  • deferred: The deferred promise for the request.

Some of the use cases of the responseInterceptor are handling wrapped responses and enhancing response elements with more methods among others.

The responseInterceptor must return the restangularized data element.

setRequestInterceptor

The requestInterceptor is called before sending any data to the server. It's a function that must return the element to be requested. This function receives the following arguments:

  • element: The element to send to the server.
  • operation: The operation made. It'll be the HTTP method used except for a GET which returns a list of element which will return getList so that you can distinguish them.
  • what: The model that's being requested. It can be for example: accounts, buildings, etc.
  • url: The relative URL being requested. For example: /api/v1/accounts/123

setFullRequestInterceptor

The fullRequestInterceptor is similar to the requestInterceptor but more powerful. It lets you change the element, the request parameters and the headers as well.

It's a function that receives the same as the requestInterceptor plus the headers and the query parameters (in that order).

It must return an object with the following properties: * headers: The headers to send * params: The request parameters to send * element: The element to send * httpConfig: The httpConfig to call with

setErrorInterceptor

The errorInterceptor is called whenever there's an error. It's a function that receives the response as a parameter.

The errorInterceptor function, whenever it returns false, prevents the promise linked to a Restangular request to be executed. All other return values (besides false) are ignored and the promise follows the usual path, eventually reaching the success or error hooks.

The feature to prevent the promise to complete is usefull whenever you need to intercept each Restangular error response for every request in your AngularJS application in a single place, increasing debugging capabilities and hooking security features in a single place.

setRestangularFields

Restangular required 3 fields for every "Restangularized" element. These are:

  • id: Id of the element. Default: id
  • route: Name of the route of this element. Default: route
  • parentResource: The reference to the parent resource. Default: parentResource
  • restangularCollection: A boolean indicating if this is a collection or an element. Default: restangularCollection
  • cannonicalId: If available, the path to the cannonical ID to use. Usefull for PK changes
  • etag: Where to save the ETag received from the server. Defaults to restangularEtag
  • selfLink: The path to the property that has the URL to this item. If your REST API doesn't return a URL to an item, you can just leave it blank. Defaults to href

Also all of Restangular methods and functions are configurable through restangularFields property. All of these fields except for id and selfLink are handled by Restangular, so most of the time you won't change them. You can configure the name of the property that will be binded to all of this fields by setting restangularFields property.

setMethodOverriders

You can now Override HTTP Methods. You can set here the array of methods to override. All those methods will be sent as POST and Restangular will add an X-HTTP-Method-Override header with the real HTTP method we wanted to do.

setDefaultRequestParams

You can set default Query parameters to be sent with every request and every method.

Additionally, if you want to configure request params per method, you can use requestParams configuration similar to $http. For example RestangularProvider.requestParams.get = {single: true}.

Supported method to configure are: remove, get, post, put, common (all)

setFullResponse

You can set fullResponse to true to get the whole response every time you do any request. The full response has the restangularized data in the data field, and also has the headers and config sent. By default, it's set to false.

setDefaultHeaders

You can set default Headers to be sent with every request.

setRequestSuffix

If all of your requests require to send some suffix to work, you can set it here. For example, if you need to send the format like /users/123.json you can add that .json to the suffix using the setRequestSuffix method

setUseCannonicalId

You can set this to either true or false. By default it's false. If set to true, then the cannonical ID from the element will be used for URL creation (in DELETE, PUT, POST, etc.). What this means is that if you change the ID of the element and then you do a put, if you set this to true, it'll use the "old" ID which was received from the server. If set to false, it'll use the new ID assigned to the element.

setEncodeIds

You can set here if you want to URL Encode IDs or not. By default, it's true.

Accessing configuration

You can also access the configuration via RestangularProvider and Restangular via the configuration property if you don't want to use the setters. Check it out:

How to configure them globally

You can configure this in either the config or the run method. If your configurations don't need any other services, then I'd recommend you do them in the config. If your configurations depend on other services, you can configure them in the run using Restangular instead of RestangularProvider

Configuring in the config

Configuring in the run

How to create a Restangular service with a different configuration from the global one

Let's assume that for most requests you need some configuration (The global one), and for just a bunch of methods you need another configuration. In that case, you'll need to create another Restangular service with this particular configuration. This scoped configuration will inherit all defaults from the global one. Let's see how.

Methods description

There are 3 sets of methods. Collections have some methods and elements have others. There are are also some common methods for all of them

Restangular methods

These are the methods that can be called on the Restangular object. * one(route, id): This will create a new Restangular object that is just a pointer to one element with the route route and the specified id. * all(route): This will create a new Restangular object that is just a pointer to a list of elements for the specified path. * oneUrl(route, url): This will create a new Restangular object that is just a pointer to one element with the specified URL. * allUrl(route, url): This creates a Restangular object that is just a pointer to a list at the specified URL. * copy(fromElement): This will create a copy of the from element so that we can modified the copied one. * restangularizeElement(parent, element, route, queryParams): Restangularizes a new element * restangularizeCollection(parent, element, route, queryParams): Restangularizes a new collection

Element methods

  • get([queryParams, headers]): Gets the element. Query params and headers are optionals
  • getList(subElement, [queryParams, headers]): Gets a nested resource. subElement is mandatory. It's a string with the name of the nested resource (and URL). For example buildings
  • put([queryParams, headers]): Does a put to the current element
  • post(subElement, elementToPost, [queryParams, headers]): Does a POST and creates a subElement. Subelement is mandatory and is the nested resource. Element to post is the object to post to the server
  • remove([queryParams, headers]): Does a DELETE
  • head([queryParams, headers]): Does a HEAD
  • trace([queryParams, headers]): Does a TRACE
  • options([queryParams, headers]): Does a OPTIONS
  • patch(object, [queryParams, headers]): Does a PATCH
  • one(route, id): Used for RequestLess connections and URL Building. See section below.
  • all(route): Used for RequestLess connections and URL Building. See section below.
  • several(route, ids*): Used for RequestLess connections and URL Building. See section below.
  • oneUrl(route, url): This will create a new Restangular object that is just a pointer to one element with the specified URL.
  • allUrl(route, url): This creates a Restangular object that is just a pointer to a list at the specified URL.
  • getRestangularUrl(): Gets the URL of the current object.
  • getRequestedUrl(): Gets the real URL the current object was requested with (incl. GET parameters). Will equal getRestangularUrl() when no parameters were used, before calling get(), or when using on a nested child.
  • getParentList(): Gets the parent list to which it belongs (if any)
  • clone(): Copies the element
  • withHttpConfig(httpConfig): It lets you set a configuration for $http only for the next call. Check the Local Config HTTP section for an example.

Collection methods

  • getList([queryParams, headers]): Gets itself again (Remember this is a collection).
  • get([id]): Gets one item from the collection by id.
  • post(elementToPost, [queryParams, headers]): Creates a new element of this collection.
  • head([queryParams, headers]): Does a HEAD
  • trace: ([queryParams, headers]): Does a TRACE
  • options: ([queryParams, headers]): Does a OPTIONS
  • patch(object, [queryParams, headers]): Does a PATCH
  • remove([queryParams, headers]): Does a DELETE
  • putElement(idx, params, headers): Puts the element on the required index and returns a promise of the updated new array
  • getRestangularUrl(): Gets the URL of the current object.
  • getRequestedUrl(): Gets the real URL the current object was requested with (incl. GET parameters). Will equal getRestangularUrl() when no parameters were used, before calling getList(), or when using on a nested child.
  • one(route, id): Used for RequestLess connections and URL Building. See section below.
  • all(route): Used for RequestLess connections and URL Building. See section below.
  • several(route, ids*): Used for RequestLess connections and URL Building. See section below.
  • oneUrl(route, url): This will create a new Restangular object that is just a pointer to one element with the specified URL.
  • allUrl(route, url): This creates a Restangular object that is just a pointer to a list at the specified URL.
  • clone(): Copies the collection
  • withHttpConfig(httpConfig): It lets you set a configuration for $http only for the next call. Check the Local Config HTTP section for an example.

Custom methods

  • customGET(path, [params, headers]): Does a GET to the specific path. Optionally you can set params and headers.
  • customGETLIST(path, [params, headers]): Does a GET to the specific path. In this case, you expect to get an array, not a single element. Optionally you can set params and headers.
  • customDELETE(path, [params, headers]): Does a DELETE to the specific path. Optionally you can set params and headers.
  • customPOST([elem, path, params, headers]): Does a POST to the specific path. Optionally you can set params and headers and elem. Elem is the element to post. If it's not set, it's assumed that it's the element itself from which you're calling this function.
  • customPUT([elem, path, params, headers]): Does a PUT to the specific path. Optionally you can set params and headers and elem. Elem is the element to post. If it's not set, it's assumed that it's the element itself from which you're calling this function.
  • customOperation(operation, path, [params, headers, elem]): This does a custom operation to the path that we specify. This method is actually used from all the others in this subsection. Operation can be one of: get, post, put, delete, head, options, patch, trace
  • addRestangularMethod(name, operation, [path, params, headers, elem]): This will add a new restangular method to this object with the name name to the operation and path specified (or current path otherwise). There's a section on how to do this later.

Let's see an example of this:

Copying elements

Before modifying an object, we sometimes want to copy it and then modify the copied object. We can't use angular.copy for this because it'll not change the this binded in the functions we add to the object. In this cases, you must use Restangular.copy(fromElement).

Enhanced promises

Restangular uses enhanced promises when returning. What does this mean? All promises returned now have 2 additional methods and collection promises have 3. These are the methods:

  • call(methodName, params*): This will return a new promise of the previous value, after calling the method called methodName with the parameters params.
  • get(fieldName): This will return a new promise for the type of the field. The param of this new promise is the property fieldName from the original promise result.
  • push(object): This method will only be in the promises of arrays. It's a subset of the call method that does a push.
  • $object: This returns the reference to the object that will be filled once the server responds a value. This means that if you call getList this will be an empty array by default. Once the array is returned from the server, this same $object property will get filled with results from the server.

I know these explanations are quite complicated, so let's see an example :D.

Using values directly in templates

Since Angular 1.2, Promise unwrapping in templates has been disabled by default and will be depracated soon.

This means that the following will cease to work:

As this was a really handy way of working with Restangular, I've made a feature similar to $resource that will enable this behavior again:

The $object property is a new property I've added to promises. By default, it'll be an empty array or object. Once the sever has responded with the real value, that object or array is filled with the correct response, therefore making the ng-repeat work :). Pretty neat :D

Using Self reference resources

A lot of REST APIs return the URL to self of the element that you're querying. You can use that with Restangular so that you don't have to create the URLs yourself, but use the ones provided by the server.

Let's say that when doing a GET to /people you get the following

In this case, as you can see, the URL to each element can't be guessed so we need to use that to reference the element. Restangular supports both relative and absolute URLs :).

How do we do this with Restangular?

First, we need to configure the path for the link to self. For that, in the config we do:

Then, we can just use this :)

URL Building

Sometimes, we have a lot of nested entities (and their IDs), but we just want the last child. In those cases, doing a request for everything to get the last child is overkill. For those cases, I've added the possibility to create URLs using the same API as creating a new Restangular object. This connections are created without making any requests. Let's see how to do this:

Using local $http configuration

There're sometimes when you want to set a specific configuration $http configuration just for one Restangular's call. For that, you can use withHttpConfig. You must call that method just before doing the HTTP request. Let's learn how to use it with the following example:

Creating new Restangular Methods

Let's assume that your API needs some custom methods to work. If that's the case, always calling customGET or customPOST for that method with all parameters is a pain in the ass. That's why every element has a addRestangularMethod method.

This can be used together with the hook addElementTransformer to do some neat stuff. Let's see an example to learn this:

Adding Custom Methods to Collections

Create custom methods for your collection using Restangular.extendCollection(). This is an alias for:

Example:

Adding Custom Methods to Models

Create custom methods for your models using Restangular.extendModel(). This is an alias for:

Example:

FAQ

How can I handle errors?

Errors can be checked on the second argument of the then.

I need to send one header in EVERY Restangular request, how do I do this?

You can use defaultHeaders property for this or $httpProvider.defaults.headers, whichever suits you better. defaultsHeaders can be scoped with withConfig so it's really cool.

Can I cache requests?

$http can cache requests if you send the property cache to true. You can do that for every Restangular request by using defaultHttpFields property. This is the way:

Can it be used in $routeProvider.resolve?

Yes, of course. Every method in Restangular returns a promise so this can be used without any problem.

How can I send a delete WITHOUT a body?

You must add a requestInterceptor for this.

My response is actually wrapped with some metadata. How do I get the data in that case?

So, let's assume that your data is the following:

In this case, you'd need to configure Restangular's responseExtractor and listTypeIsArray. See the following:

I use Mongo and the ID of the elements is _id not id as the default. Therefore requests are sent to undefined routes

What you need to do is to configure the RestangularFields and set the id field to _id. Let's see how:

How do I handle CRUD operations in a List returned by Restangular?

The best option for doing CRUD operations with a list, is to actually use the "real" list, and not the promise. It makes it easy to interact with it.

Let's see an example :).

When you actually get a list by doing

You're actually assigning a Promise to the owners value of the $scope. As Angular knows how to process promises, if in your view you do an ng-repeat of this $scope variable, results will be shown once the promise is resolved (Response arrived). However, changes to that promise that you do from your HTML won't be seen in the scope, as it's not a real array. It's just a promise of an array.

When I set baseUrl with a port, it's stripped out.

It won't be stripped out anymore as I've ditched $resource :). Now you can happily put the port :).

How can I access the unrestangularized element as well as the restangularized one?

In order to get this done, you need to use the responseExtractor. You need to set a property there that will point to the original response received. Also, you need to actually copy this response as that response is the one that's going to be restangularized later

Addendum : If you want originalElement to be the original response object instead of having an original value for each key in your newResponse array, replace

By

Why does this depend on Lodash / Underscore?

This is a very good question. I could've done the code so that I don't depend on Underscore nor Lodash, but I think both libraries make your life SO much easier. They have all of the "functional" stuff like map, reduce, filter, find, etc. With these libraries, you always work with immutable stuff, you get compatibility for browsers which don't implement ECMA5 nor some of these cool methods, and they're actually quicker. So, why not use it? If you've never heard of them, by using Restangular, you could start using them. Trust me, you're never going to give them up after this!

Supported Angular versions

Restangular supports both 1.0.X and 1.1.X up to versions 1.0.8 and 1.1.5.

Also, when using Restangular with version >= 1.1.4, in case you're using Restangular inside a callback not handled by Angular, you have to wrap the whole request with $scope.apply to make it work or you need to run one extra $digest manually. Check out https://github.com/mgonto/restangular/issues/71

Server Frameworks

Users reported that this server frameworks play real nice with Restangular, as they let you create a Nested Restful Resources API easily:

  • Ruby on Rails
  • CakePHP, Laravel and FatFREE for PHP
  • Play1 & 2 for Java & scala
  • Restify and Express for NodeJS
  • Tastypie for Django
  • Slim Framework

Releases Notes

New releases notes are together with releases in GitHub at: https://github.com/mgonto/restangular/releases

To see old releases notes, you can click here

License

The MIT License

Copyright (c) 2013 Martin Gontovnikas http://www.gon.to/

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

comments powered by Disqus
This page was last updated 5 months ago.