• Background

  • Basic RESTful API concept implementation via the ngx-restangular

  • Why should you use the ngx-restangular?

    1. Supports both - Promises and Observables

    2. Send a request from/within an object - don’t create a new object for each request

    3. Supports nested RESTful resources

    4. Use meaningful names instead of URLs

    5. Provides an ability to create your own HTTP methods

    6. Send requests easily using different settings

  • Conclusion


To write your own models, interceptors, modules for handling errors, methods for writing needed headers and lots of other stuff - that’s rather exciting only if you aren’t doing it a third time in 2 months, right? Actually, that’s the reason why we’ve decided to port a Martin Gontovnikas’ “Restangular” on an Angular/TypeScript.

If you want to check out the official ngx-restangular’s documentation directly or view the source code on a Github, please click here.

🤓 Basic RESTful API concept implementation via the ngx-restangular

Some web applications aim to implement the REST design pattern - a set of principles that conform to a specific architectural style using the HTTP protocol and its methods. Check out the table with imaginary 2muchcoffee REST API endpoint:

REST API Description
https://2muchcoffee.com/users Get all users
https://2muchcoffee.com/user/123 Get a specific user
https://2muchcoffee.com/user/123/products Get a products list of the specific user
https://2muchcoffee.com/user/123/products/456 Get a specific product of the selected user

From the table above you can see how the data and its nested entities are organized by the REST concept. Let’s have a look at the ngx-restangular syntax for working with the RESTful API:

export class UserComponent implements OnInit { allUsers: UserModel[] & Restangular; selectedUser: UserModel & Restangular; selectedUserProduct: ProductModel & Restangular; selectedUserProducts: ProductModel[] & Restangular; constructor( private restangular: Restangular, ) { } ngOnInit() { // A GET request to '/users' - returns a list of users this.restangular.all('users').getList().subscribe(users => this.allUsers = users ); // A GET request to '/users/123' - returns a specific user(considering 123 // is the id of the selected User) this.restangular.one('users', 123).get().subscribe(user => this.selectedUser = user ); // A GET request to '/users/123/products' - returns a products list // of the specific user this.restangular.one('users', 123).all('products').getList().subscribe(products => this.selectedUserProducts = products ); // A GET request to '/users/123/products/456' - returns a specific product // of the selected user this.restangular.one('users', 123).all('products', 456).get().subscribe(product => this.selectedUserProduct = product ); }
}

Besides, the path would be identical in case you want to add, edit and delete (POST, PUT, DELETE) a specific user or his product.

🤔 Why should you use the ngx-restangular?

The ngx-restangular can be an overkill if you're trying to implement something simple, but when we're talking about a complex web application with an army of requests to REST API - you should use it without any hesitation. It will help you easily to handle consuming data from the REST API of any complexity with a minimum of client code.

To make it short and straightforward, the main idea of the ngx-restangular is to give an Angular community a powerful but yet simple tool with all “Restangular” features:

1. Supports both - Promises and Observables:

Nowadays one could state that Functional Reactive Programming via Observables is one of the most stable and handy ways for async data and events handling in the web application. However, there are still many developers who prefer to use another approach - Promises.

With the ngx-restangular you don’t have to give up the most relevant for you approach, because RxJS provides you with both options and you can choose the most suitable one.

export class UsersComponent implements OnInit { allUsers: UserModel[] & Restangular; constructor( private restangular: Restangular, ) { } ngOnInit() { // Creating a Restangular object - just specify the base URL const baseUsers = this.restangular.all('users'); // A GET request to '/users' using an Observable baseUsers.getList().subscribe(users => this.allUsers = users); // A GET request to '/users' using a Promise baseUsers.getList().toPromise().then(users => this.allUsers = users); }
}

2. Send a request from/within an object - don’t create a new object for each request:

Once you have got an object by specifying the base URL, you are able to send further server requests using that object. Thus, you don’t have to remember and specify the URL each time. Check out the code example below:,

export class UsersComponent implements OnInit { allUsers: UserModel[] & Restangular; baseUsers: Restangular; newUser: FormGroup<UserModel> = new FormGroup({ firstName: new FormControl('John'), lastName: new FormControl('Silva'), }); constructor( private restangular: Restangular, ) { } ngOnInit() { // Creating a Restangular object by specifying the base URL this.baseUsers = this.restangular.all('users'); // A GET request to '/users' - returns a list of users this.baseUsers.getList().subscribe(users => this.allUsers = users); } submitNewUser() { // A POST request to '/users' - adds a new user this.baseUsers.post(this.newUser.value); }
}

3. Supports nested RESTful resources:

The ngx-restangular has built-in features to work with nested entities. Let’s imagine that the “UsersComponent” from the first example passes into the “UserComponent” the selected User data.

The “UserComponent” itself is responsible for displaying the data of the User, including the user’s products list that you have to request from the API first. Thanks to the ngx-restangular you don’t have to specify the URL again and in order to get the user’s products from the API, you can just use the “selectedUser” object. Isn’t that amazing? Especially if you have a quite deep nesting, e.g. you need to get the vendor of the first product that selected User has.

Have a look at the example below:

export class UserComponent implements OnInit @Input('user') selectedUser: UserModel & Restangular; firstProduct: ProductModel & Restangular; selectedUserProducts: ProductModel[] & Restangular; ngOnInit() { // A GET request to ‘/users/123/products considering 123 is the id of // the selected User
Returns a products list of the specific user this.selectedUser.getList('products').subscribe(products => { this.selectedUserProducts = products; this.firstProduct = products[0]; }); }
}
export class ProductComponent implements OnInit { @Input('product') selectedProduct: ProductModel & Restangular; selectedProductVendors: VendorModel[] & Restangular; ngOnInit() { // A GET request to '/users/123/products/789/vendors considering 123 is the id of the // selected User, 789 is the id of the selected first Product - returns // a vendors list of the specific user this.selectedProductVendors = this.selectedProduct.getList('vendors'); }
}

4. Use meaningful names instead of URLs:

Once you receive an object from an URL you can use meaningful names to access the data next time. You don’t need to remember each URL you use.

export class UsersComponent implements OnInit { constructor( private restangular: Restangular, ) { } ngOnInit() { // Creating a Restangular object by specifying the base URL const baseUsers = this.restangular.all('users'); // A GET request to '/users' - returns a list of users baseUsers.getList().subscribe(users => { const firstUser = users[0]; firstUser.firstName = 'Sam'; // A PUT request to '/users/777' considering 777 is the id of the first User firstUser.put(); }); }
}

5. Provides an ability to create your own HTTP methods:

You can simply add custom HTTP methods with unique logic and receive what you need with a server response. The ngx-restangular’s “getList” and “get” methods, as an example, await a collection or an object respectively as a response by default. And in order to receive a response in a diverse format, e.g. as a string, or to specify additional parameters to the request, you can create a custom HTTP “GET” method. Let’s have a look at the first example:

 // A GET request to '/users/123/messages?param=myParam' this.selectedUser.customGET('messages', {param: 'myParam'});

OR here’s an example for sending files:

 // A POST request to ‘/users’
baseUsers.customPOST(formData, undefined, undefined, {'Content-Type': undefined});

That basically tells the request to use the Content-Type: multipart/form-data as a header. Also, formData is the body of the request, you have to add all the params here, of course including the File you want to send.

6. Send requests easily using different settings:

Most often you have only one API and one global configuration for that in the project. But let’s imagine that you have more than one API or you just need to make a bunch of requests with another configuration, e.g. diverse base URL, headers etc. And what a coincidence! You can also easily implement that with the ngx-restangular!

The ngx-restangular allows you to set a configuration with various settings and use it in different parts of your application. Thus, you can generate diverse requests for different APIs without any concerns. In such a case, you just need to create another Restangular service with its own particular configuration. In addition, this scoped configuration will inherit all defaults from the global one. Take a look at the example below:

// Function for setting the default Restangular configuration
export function RestangularConfigFactory (RestangularProvider) { RestangularProvider.setBaseUrl('https://2muchcoffee.com');
}
//Restangular service that uses accounting
export const RESTANGULAR_ACCOUNTING = new InjectionToken<any>('RestangularAccounting');
export function RestangularAccountingFactory(restangular: Restangular) { return restangular.withConfig((RestangularConfigurer) => { RestangularConfigurer.setBaseUrl('https://accounting.2muchcoffee.com'); });
}
// AppModule is the main entry point into Angular bootstrapping process
@NgModule({ bootstrap: [ AppComponent ], declarations: [ AppComponent ], imports: [ // Global configuration RestangularModule.forRoot(RestangularConfigFactory), ], providers: [ { provide: RESTANGULAR_ACCOUNTING, useFactory: RestangularAccountingFactory, deps: [Restangular] }, ]
})
export class AppModule {}
// Let's use it in the users component
@Component({ ...
})
export class UsersComponent { constructor( @Inject(Restangular) private Restangular, @Inject(RESTANGULAR_ACCOUNTING) private RestangularAccounting, ) { } ngOnInit() { // A GET request to https://2muchcoffee.com/users // Uses global configuration Restangular.all('users').getList() // A GET request to https://accounting.2muchcoffee.com/users // Uses Accounting configuration which is based on Global one, therefore .json // is added. RestangularAccounting.all('users').getList() }
};

Conclusion

The ngx-restangular is a custom Angular solution for any web application with an absolutely clear structure and many features that make the REST API much easier. It’s totally proved by hundreds of MVP and start-ups that have already used it in their Angular 2/4/5/6/7 based projects. Currently, our project has already got over 650 stars on a Github.

Welcome to the ngx-restangular family and don’t hesitate to reach us through the ngx-restangular contact form in case you have any questions or suggestions. Moreover, if you have a clear vision of how to improve the current functionality with features that in your opinion are missing within a HttpClientModule - feel free to contribute by contacting us. We will be more than happy to assist you.