Monthly Archives: March 2018

How to Optimize Node Requests with Simple Caching Strategies

By Chris Nwamba

First run without caching gave a response time of 3043ms

One of the things that affect how users interact with our applications is its speed. Even though some users generally have a poor connection, they are expecting some minimum level of speed when using the application. To give our users a seamless experience, we are going to consider the possibility of optimizing our requests in node by using the concept of caching.

Trust me, your users will thank you.

In this article, we are going to look at how to optimize requests in our Node applications by caching responses to requests. Now, you may be wondering what caching is all about.
Caching generally involves storing and retrieving data from a high-performance memory

Why Use A Cache

The goal of caching is to save your server the stress of having to do the same thing over and over again that results in the same output. Let’s take for example, you have a page that generates a report of a companies inventory for the previous day for accountability. Now, let’s imagine that the report page will be viewed at least a thousand times daily by different auditors. Generating a single report wouldn’t be a problem, but if the same report is going to be generated every single time a thousand times, then you need to look for a better way to do it and that’s where caching comes in.

Requirements

To enable you to follow through the article, you need to have the following :

  • Node installed on your machine,
  • Node Package Manager ( NPM ) installed on your machine
  • Some Basic Javascript Knowledge

To confirm your installations, run the following in your terminal :

    node --version
    npm --version

If you get version numbers as result, then you’re good to go.

Building A Basic Inventory Application

We need to make a simple inventory reporting application to test caching capabilities with. The main goal is to look at the different approaches to caching in our node applications.

Since you already have npm installed on your machine, create a new project folder and install the necessary packages :

    mkdir inventory-app
    cd inventory-app 
    npm init && npm install --save express memory-cache flat-cache redis sqlite3

Let’s take a look at the index.js.

Import express and sqlite:

    // index.js

    const express = require('express')
    const sqlite3 = require('sqlite3').verbose();
    const PORT = process.env.PORT || 3128 ;
    const app = express();

    [...]

express is a routing framework for Node which simplifies HTTP task. sqlite3 will power the database for this app. The application port is also specified and an express application is created.

Next thing we need to do is to create the server route. Just one route that points to a /products endpoint:

    // index.js
    [...]

    app.get('/products',  function(req, res){
        setTimeout(() => {
          let db = new sqlite3.Database('./NodeInventory.db');
          let sql = `SELECT * FROM products`;

          db.all(sql, [], (err, rows) => {
              if (err) {
                  throw err;
              }
              db.close();
              res.send( rows );
          });
          // this was wrapped in a setTimeout function to intentionally simulate a slow 
          // request
        }, 3000);
    });

    [...]

When a request is made to the /products route, a connection is made to our sqlite database and then the query’s executed to fetch all our products. Once this is done, the connection is closed and the response is sent back to the user with all the products that exist in our inventory.

The sqlite file is in the Github repository for reference.

To start listening to requests on the port, add the following at the end:

    // index.js
    [...]

    app.listen(PORT, function(){
        console.log(`App running on port ${PORT}`);
    });

    [...]

Head to Postman or the browser and visit the /products route to get a list of the items stored in the database:

So far our single-route app works, but imagine having to do this over and over again which is what will be the case when our daily inventory is being reviewed over a hundred times. Let’s take a look at how to optimize requests by making use of some caching functions

Different Caching Options

Using Memory-Cache for caching ( In – Memory )
Earlier on, the memory-cache module was installed. Now, let’s look at how to use it.

Import the module:

    // index.js
    [...]

    cache cache = require('memory-cache');

    [...]

Create and configure cache middleware :

    // index.js

    // configure cache middleware
    let memCache = new cache.Cache();
    let cacheMiddleware = (duration) => {
        return (req, res, next) => {
            let key =  '__express__' + req.originalUrl || req.url
            let cacheContent = memCache.get(key);
            if(cacheContent){
                res.send( cacheContent );
                return
            }else{
                res.sendResponse = res.send
                res.send = (body) => {
                    memCache.put(key,body,duration*1000);
                    res.sendResponse(body)
                }
                next()
            }
        }
    }

    // app routes
    [...]

The duration representing how long the values need to stored in the cache will be specified. In the middleware, a unique key based on the request url is generated and a check is made to see if there is already content stored for that key. If content exists, the data is sent back as the response without having to make the extra query to our database.

Now, if there is no content in the cache for the particular key, the request is processed as usual and the result of the request is stored in our cache before the response is sent to the user.

To use the cacheMiddleware edit the index.js to look like this :

    // index.js

    app.get('/products', cacheMiddleware(30), function(req, res){
         [...]    
    });

Notice how specified the duration we want the response for the request to be cached for was specified

We just looked at in-memory method for caching responses. One of the downsides of this method is that once the server goes down, all cached content is lost.

Returns Products with a response time of 3045ms

Now, let’s take a look at the using a caching method that persists the data on the server even after it has been restarted.

Using Flat file for caching
We are going to now take a look at how to use files to persist our cached data on the server. To do this, we are going to make use of this package – flat-cache

Install the module by running :

    npm install --save flat-cache

Now, tweak the index.js to make use of our new module. The index.js will look like this :

    // index.js
    [...]

    const flatCache = require('flat-cache')

    [...]

Then the new cache is loaded

    // index.js 
    [...]

    // load new cache
    let cache = flatCache.load('productsCache');
    // optionally, you can go ahead and pass the directory you want your
    // cache to be loaded from by using this
    // let cache = flatCache.load('productsCache', path.resolve('./path/to/folder')

    [...]

Now that the cache has been loaded, the next thing to do now is to edit our application route so :

    // index.js
    [...]

    // create flat cache routes
    let flatCacheMiddleware = (req,res, next) => {
        let key =  '__express__' + req.originalUrl || req.url
        let cacheContent = cache.getKey(key);
        if( cacheContent){
            res.send( cacheContent );
        }else{
            res.sendResponse = res.send
            res.send = (body) => {
                cache.setKey(key,body);
                cache.save();
                res.sendResponse(body)
            }
            next()
        }
    };

    // create app routes 

    [...]

The logic behind this is similar to the in-memory cache. Check if a response already exists for our request using the key and it there already exists a response, it is returned to the user else, the request is executed and the response is saved to the cache before going to the user.

Notice that we ran the cache.save() . Doing this specifies that the cache is saved as a file in the directory we specified – if you specified any. If no directory was specified the module will determine where to save the ‘cache-file’

To use the flatCacheMiddleware , edit the application routes so :

    // index.js
    [...]

    // create app routes

    app.get('/products', flatCacheMiddleware, function(req, res){
      [...]
    });

Now, when the /products route is visited, the following result is obtained :

Products are returned, with a response time of  3047ms

A productsCache file is also created with the following content :

    {"__express__/products":"[{"ID":1,"NAME":"Shoes","COUNT":20},{"ID":2,"NAME":"Bags","COUNT":12},{"ID":3,"NAME":"Tables","COUNT":50},{"ID":5,"NAME":"Laptop","COUNT":140},{"ID":6,"NAME":"Chair","COUNT":200},{"ID":7,"NAME":"Mouse","COUNT":20},{"ID":8,"NAME":"Pen","COUNT":1000},{"ID":9,"NAME":"Chips","COUNT":6303},{"ID":10,"NAME":"Slides","COUNT":22},{"ID":11,"NAME":"Console","COUNT":32}]"}

Using MemCached ( A Service )
Another option to consider for caching is memcached. Memcached is a caching client built for node JS with scaling in mind.

To use the Memcached node client, you need to have memcached installed on your machine. Head over here to get it installed.
When you have it installed, you then install the memcached node client by running :

    npm install --save memcached

Now that the client is installed, import the module in the index.js:

    // index.js 
    [...]

    const Memcached = require('memcached');

    [...]

Now, go ahead and configure the memcachedMiddleware by tweaking the index.js to look as follows :

    // index.js
    [...]

    let memcached = new Memcached("127.0.0.1:11211")

    let memcachedMiddleware = (duration) => {
        return  (req,res,next) => {
        let key = "__express__" + req.originalUrl || req.url;
        memcached.get(key, function(err,data){
            if(data){
                res.send(data);
                return;
            }else{
                res.sendResponse = res.send;
                res.send = (body) => {
                    memcached.set(key, body, (duration*60), function(err){
                        // 
                    });
                    res.sendResponse(body);
                }
                next();
            }
        });
    }
    };

    [...]

A memcached object is created and the object is connected to the PORT our Memcached instance is running on – in this case, 11211. Afterwards, a memcachedMiddleware is also created which is similar to the other middlewares created earlier. If there is no content for the specified key in the cache, the request is completed as usual and the response is stored in our cache. If there is a response for the specified key, the content is obtained from the cache and returned to the user without having to process the request.

Now, edit the /products route and update it to use the memcachedMiddleware :

    // index.js
    [...]

    app.get("/products", memcachedMiddleware(20), function(req, res) {
      [...]
    });

    [...]

The memcachedMiddleware(20) above, specifies the duration ( in minutes ) for how long the key is to be stored in the cache.

When a request is made to the /products route from Postman, the following result is obtained :

Returns the products with a response time of 3049ms

Using Redis for caching
Now, we have seen how to use in-memory cache and also how to persist our caches using files. Let’s take a look at using redis for caching in our application. Redis stands for **RE**``mote **D**``ictionary **S**``erver that has the ability to store and manipulate high-level data types.

To get started using redis for caching application, install the redis node client by running :

    npm install -save redis

We also need to have a redis server running on our local machine. You can head over here to find out how.

When you are sure your redis server is installed and working properly, the next thing to do is to import the necessary modules and create your redis client:

    // index.js
    [...]

    const redis = require('redis')
    const client = redis.createClient();

    [...]

After this, create a redisMiddleware and that’ll look like this :

    // index.js
    [...]

    // create redis middleware
    let redisMiddleware = (req, res, next) => {
      let key = "__expIress__" + req.originalUrl || req.url;
      client.get(key, function(err, reply){
        if(reply){
            res.send(reply);
        }else{
            res.sendResponse = res.send;
            res.send = (body) => {
                client.set(key, JSON.stringify(body));
                res.sendResponse(body);
            }
            next();
        }
      });
    };

    // app routes
    [...]

A check is made to see if a response already exists for our request. If a response exists, then we fetch the value from the cache and return the response. If no response exists in the cache, then the request is executed the response is stored in the redis cache for future requests.

The /products route is also updated to use the redisMiddleware :

    // index.js 
    [...]

    app.get("/products", redisMiddleware, function(req, res) {
      [...]
    });

    // set port
    [...]

When a request to the /products server, you get :

Returns the products with a response time of 3049ms

Results and Recommendation

Now, we are going to compare performance for the different approaches we have taken in this article :

  • No Caching at all
  • In-Memory Caching
  • Caching to File
  • Using Memcached
  • Using Redis for Caching

  • No Caching at All
Run Response Time
Ist Run 3043ms
2nd Run 3027ms
3rd Run 3019ms
4th Run 3009ms
Avg Time 3024.5ms
  • In-Memory Caching
Run Response Time
Ist Run 3045ms
2nd Run 23ms
3rd Run 4ms
4th Run 17ms
Avg Time 772.25ms
  • Caching To File
Run Response Time
Ist Run 3047ms
2nd Run 31ms
3rd Run 8ms
4th Run 13ms
Avg Time 774.75ms
  • Using MemCached
Run Response Time
Ist Run 3049ms
2nd Run 9ms
3rd Run 10ms
4th Run 16ms
Avg Time 771ms
  • Using Redis for Caching
Run Response Time
Ist Run 3049ms
2nd Run 6ms
3rd Run 18ms
4th Run 10ms
Avg Time 770.75ms

From the results above, we can see that deciding not caching the files at all does not help improve the response time. The different caching options we have looked at so far have proven to be successful in reducing the average load time.

Conclusion

Caching comes in handy a lot of times, but you need to know when to use it. it may be an overkill especially when the requests made to the application aren’t frequent. Also, POST, PUT and DELETE methods should never be cached because you want unique resources to be affected each time a request is made.

Here’s a link to the GitHub repository. Feel free to leave your comments and questions below

Source:: scotch.io

How Pipedrive builds its back-end with Node.js & PHP

By Tamas Kadlecsik

How Pipedrive builds its back-end with Node.js & PHP

At RisingStack, we are highly interested in building scalable and resilient software architectures. We know that a lot of our readers share our enthusiasm, and that they want to learn more about the subject too.

To expand our blogging & training initiatives, we decided to launch a new series called Top of the Stack which focuses on architecture design, development trends & best practices for creating scalable applications.

In the third episode of the Top of the Stack Series, we interviewed Kristo Kaiv, the head of architecture at Pipedrive!

During our interview with Kristo Kaiv from Pipedrive, we discussed a wide range of topics. We talked about the languages & databases they use, how they handle monitoring and testing, where their applications are deployed and what scaling problems they experience in their current architecture.

Previously on Top of the Stack

  • In the first episode of Top of the Stack, we interviewed Patrick Kua, the CTO of N26, a successful banking startup from Germany which just recently got a series C funding of $160M.

  • In the second episode, we interviewed Angel Cereijo & Roberto Ansuini from Fintonic, and discused how they use Node, MongoDB & Kubernetes to scale!

Source:: risingstack.com

How Pipedrive builds it's back-end with Node.js & PHP

By Tamas Kadlecsik

How Pipedrive builds it's back-end with Node.js & PHP

At RisingStack, we are highly interested in building scalable and resilient software architectures. We know that a lot of our readers share our enthusiasm, and that they want to learn more about the subject too.

To expand our blogging & training initiatives, we decided to launch a new series called Top of the Stack which focuses on architecture design, development trends & best practices for creating scalable applications.

In the third episode of the Top of the Stack Series, we interviewed Kristo Kaiv, the head of architecture at Pipedrive!

During our interview with Kristo Kaiv from Pipedrive, we discussed a wide range of topics. We talked about the languages & databases they use, how they handle monitoring and testing, where their applications are deployed and what scaling problems they experience in their current architecture.

Previously on Top of the Cast

  • In the first episode of Top of the Stack, we interviewed Patrick Kua, the CTO of N26, a successful banking startup from Germany which just recently got a series C funding of $160M.

  • In the second episode, we interviewed Angel Cereijo & Roberto Ansuini from Fintonic, and discused how they use Node, MongoDB & Kubernetes to scale!

Source:: risingstack.com

Code Challenge #7: Build An Avengers Off Canvas Sidebar

By William Imoh

Previously on the code challenge #6, we built a Pomodoro timer using Vue.js. Here is the solution to the challenge. Also, check out the comments under the post for awesome submissions.

In this challenge, we are required to build a stylish but simple Off Canvas Sidebar. Sidebars are great for easy navigation across single page websites and applications and a fantastic UI improvement will be to build it out using Off Canvas.

The Challenge

In this challenge, we are required to create an Off Canvas sidebar, which slides in with the click of the menu button and slides out with the click of the same button. Feel free to build this out using any tool or technology. The final product should look like this:

As can be seen, the goal of the challenge is to slide in the sidebar with the click of a button and slide it out with the click of another button.

Note the transition on the sidebar when it slides in and slides out

Requirements

To make this challenge as fun as possible, feel free to build this out with any tool or technology of your choice. Want to go the extra mile? You could throw in some images and a bio for your favorite badass superhero. Also, take note of the following requirements.

  1. The sidebar should slide in with the click of a button and also slide out with the click of the same button.
  2. When the sidebar slides in, the content of the website should fit in completely on the screen and shouldn’t overflow.
  3. Control transition of the sidebar when the button is clicked.
  4. Have loads of fun while completing this challenge.

Goals for this Challenge

  • Understand Off Canvas and how it works.
  • Utilize transition properties in CSS
  • Build a stylish sidebar

Bonus

As a bonus, you could include a close-menu button and also provide a smooth transition between it and the menu button when clicked.

Resources

A base codepen is provided containing HTML and CSS code. CSS variables are also used with the Sass variant. Feel free to modify it if you like.

Fork this base codepen as your starting point

Community

  • Post your solutions in the comment section of this post.
  • Join the Slack group to interact with others in the #codechallenge channel and also post your solution there.
  • Post your solution on twitter using the hashtag #scotchchallenge

Solution

The solution to this challenge will be released on Sunday. Happy Coding!

Source:: scotch.io

Migrate Your AngularJS Services to Angular with ngUpgrade

By Sam Julien

In our last guide, we covered how to install everything we need to get started with upgrading from AngularJS to Angular. We also covered the basics of rewriting and downgrading components.

Now, let’s learn how to work with services in an ngUpgrade project. You’ll learn how to:

  • Rewrite an AngularJS service to Angular
  • Convert an observable to a promise
  • Downgrade the service so it still works with our AngularJS code
  • Convert a promise to an observable

Our Starting Point

Take a minute to clone or fork this sample project on GitHub (don’t forget to run npm install in both the public and server folders). Checkout this commit to see our starting point:

git checkout 083ee533d44c05db003413186fbef41f76466976

We’ve got a simple Order System project that we can use to work through ngUpgrade. It’s using component architecture, TypeScript, and Webpack (with builds for both development and production). We’ve also got Angular and ngUpgrade set up and bootstrapped, and the home component has been rewritten to Angular.

(If you’re lost on any of that, we cover it all in the comprehensive video course Upgrading AngularJS.)

One quick note: Things change quickly in Angular and RxJS. If you’re using Angular 4.3+ or 5+, you’ll see a couple slight discrepancies here compared to the sample project. The sample project uses Http in services for HTTP calls like GET and POST. We’re going to use the new, much simpler HttpClient that was added as of version 4.3+. For the purposes of this tutorial, the functionality is basically the same, so just follow along here. RxJS also made some changes as of version 5.5 in the way things are imported, so we’ll use that new style here.

Let’s Rewrite an AngularJS Service

When doing an ngUpgrade, it’s smart to pick one route at a time and work from the bottom up. This is because it’s very easy to make Angular code available to AngularJS, and we can take advantage of keeping Angular and AngularJS running side by side without worrying about breaking the app.

Since we did the home route in the last guide, we’re now ready to start on the customers route. We’ll start by rewriting the CustomerService and downgrading it to make it available to our AngularJS components. Then, we’ll take a look at using both observables and promises in the service, so that you can choose for yourself which will work best for you in your migration process.

Add HttpClient to NgModule

Before we rewrite the CustomerService, we have to explicitly import Angular’s HttpClientModule into our NgModule for the app (app.module.ts) in order to make HTTP calls. This is different than in Angular JS, where everything was included by default. In Angular, we need to be explicit about which parts of Angular we want to use. While it may seem inconvient at first, this is great because it helps reduce the footprint of our application by not automatically importing unused code.

So after line 3, we’ll import it like this:

import { HttpClientModule } from '@angular/common/http';

Then, we need to add that module to our imports array after the UpgradeModule on line 12:

//app.module.ts
@NgModule({
    imports: [
        BrowserModule,
        UpgradeModule,
        HttpClientModule
    ],
    declarations: [
        HomeComponent
    ],
    entryComponents: [
        HomeComponent
    ]
})

Now we’re able to use the HttpClientModule throughout our application. We only need to import it once and we can use it for all the rest of our services throughout the application.

Rewrite the Customer Service

Now that we’ve got HttpClientModule added to our Angular app module, we’re ready to rewrite the CustomerService in Angular. We’ll then downgrade it so that we can still use it in our Angular JS components as well as our Angular components.

The first thing we’ll do is rename the customerService.ts file tocustomer.service.ts so that it follows the current naming conventions.

Now, let’s open the file. You’ll see that we’re using an ES2015 class already:

//customer.service.ts
class CustomerService{
    $http: any;
    constructor($http) {
        this.$http = $http;
    }

    getCustomers(){
        return this.$http.get('/api/customers')
            .then((response) => response.data);
    }

    getCustomer(id){
        return this.$http.get(`/api/customers/${id}`)
            .then((response) => response.data);
    }

    postCustomer(customer){
        return this.$http.post('/api/customers', customer)
            .then((data) => data);
    }
}

CustomerService.$inject = ['$http'];
export default CustomerService;

Angular 2+ services are equally straightforward. They’re also just classes that we export, but we add the Injectable() annotation. Gone are the days of trying to remember factories, services, providers, and how to create each one. In Angular, a service is a service, and it’s just an exported class with the injectable annotation. Isn’t that a huge relief?

Prepare the Code

The first thing we can do is delete the last two lines in this file. We no longer need the AngularJS $inject array, and instead of using export default, we’re going to add the export keyword before the class declaration:

export CustomerService { //etc.

Now I’m ready to import two things from Angular up at the top of the file. The first is the Injectable() annotation that I mentioned:

import { Injectable } from '@angular/core';

Next we need the HttpClient:

import { HttpClient } from '@angular/common/http';

Great. Now we’re ready to make this an Angular service!

Update the Service Class to Angular

First, let’s add the Injectable() annotation to our CustomerService, just above the class:

@Injectable()

There’s no options object that gets passed into this annotation.

The next thing we need to do is replace all of our references to AngularJS’s $http service with Angular’s HttpClient. We’re going to use the shorthand http for this instead, so the easiest thing to do is a find and replace in this document of $http to http, given that most of the calls will actually largely be the same:

Now we need to change one thing about how our http property is created. Instead of this:

//customer.service.ts
class CustomerService{
    http: any;
    constructor(http) {
        this.http = http;
    }

…we’re going to delete line six that declares a public property of http of type any. Instead, in our constructor, let’s add the private keyword before http and specify that it’s of type HttpClient:

//customer.service.ts
export class CustomerService{
    constructor(private http: HttpClient) {  }

With Angular’s dependency injection, we’re instantiating a private instance of the HttpClient service on our CustomerService.You can also see that, with the private keyword, we don’t need to set our class instance of http equal to our injected instance (it does this behind the scenes for us).

What we have now is the bare bones of an Angular service, but you’ll now see those red squiggly lines underneath our everywhere we use .then. You can see that the IntelliSense is telling us that property then does not exist on type observable of response:

What’s going on there? Let’s tackle that next.

Promises or Observables?

We’ve got our customer service largely rewritten to be an Angular service, but we’ve got a little bit of a problem with trying to use .then on these http calls. That’s because the HttpClient in Angular returns an observable instead of a promise. We’ve got two choices here:

  1. The practical way: convert these responses to promises and the rest of our application will work the same, or
  2. The fun way: keep these responses as observables and update our components.

With any large scale refactor or upgrade, the goal is always to lose as little up time in your application as possible. My recommended approach is to first convert the calls to promises. That way, you can determine what components and other parts of the application are dependent on the service and its calls. After you’ve done that, you can convert the calls one at a time to observables, and update each component accordingly. So, first, get a service over to Angular and get it working. Then worry about using observables when you feel the time is right. Or, as a friend of mine says, “First migrate, then get fancy.”

So let’s first learn how to convert the calls to promises. Don’t worry though – in a bit we’ll do the fun thing and convert a call to an observable.

The toPromise Operator

To convert observables to promises, we first need to import from RxJS, the library that handles observables. After our Angular imports, we just need to add:

import { Observable } from 'rxjs/Observable';

This lets us use various functions for the observable object provided by RxJS.

The toPromise method lets us convert observables to promises. It used to be a separate import in previous versions of RxJS, but has now been rolled into Observable. Importing individual operators is a common pattern in RxJS, but figuring out which operators you need and where they reside in the library can be a little daunting. Be sure to go through the documentation resources that RxJS provides, as well as the Angular documentation on RxJS.

Now we can use the toPromise operator before each .then in our calls. When you do that, you’ll also see an error that says that .data is not a property that exists on the type “object”. That’s because the response already returns the data object inside of the HTTP response. All we need to do then is remove the .data. This is different than in the days of the original Http service, where we needed to call a .json function to return the data.

One more thing. Since we have the benefits of TypeScript, let’s add the return type to each of these functions. It’s always best in TypeScript to specify types when possible, even though, technically, it’s not required. So, after each function name, we’ll add :Promise.

The finished functions in the service will look like this:

//customer.service.ts
getCustomers():Promise<any> {
   return this.http.get('/api/customers')
       .toPromise()
        .then(response => response);
}

getCustomer(id):Promise<any> {
    return this.http.get(`/api/customers/${id}`)
        .toPromise()
        .then(response => response);
}

postCustomer(customer):Promise<any> {
    return this.http.post('/api/customers', customer)
       .toPromise()
       .then(data => data);
}

Awesome, we’ve successfully converted the observales in our calls to promises!

Downgrade Customer Service

Now that we’ve converted our observables to promises, we’re ready to downgrade the customer service so that the not-yet-migrated AngularJS components can still use it.

This process is very similar to when we downgraded the home component in the previous guide. The first thing we need to do is import the downgradeInjectable function from the ngUpgrade library, just like we imported downgradeComponent for the home component. So after line two, we’ll add:

import { downgradeInjectable } from '@angular/upgrade/static';

We also need to declare a variable called angular just like we did in our home component. So after line four, we’ll add:

declare var angular: angular.IAngularStatic;

Then at the bottom of our file, we’ll register our service as a downgraded factory. So, after the end of the class, we’ll type:

angular.module('app')
    .factory('customerService', downgradeInjectable(CustomerService));

And voíla! We’ve downgraded the CustomerService to be available to AngularJS. Here’s the finished service:

//customer.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { downgradeInjectable } from '@angular/upgrade/static';
declare var angular: angular.IAngularStatic;

@Injectable()
export class CustomerService {
    constructor(private http: HttpClient) {}

    getCustomers():Promise<any> {
        return this.http.get('/api/customers')
            .toPromise()
            .then(response => response);
    }

    getCustomer(id):Promise<any> {
        return this.http.get(`/api/customers/${id}`)
            .toPromise()
            .then(response => response);
    }

    postCustomer(customer):Promise<any> {
        return this.http.post('/api/customers', customer)
            .toPromise()
            .then((data) => data);
    }
}

angular.module('app')
    .factory('customerService', downgradeInjectable(CustomerService));

Move the Service to the Angular Module

Our customer service has been rewritten to an Angular service and downgraded to be available to AngularJS. Now we need to remove our reference in our AngularJS module and add it to our Angular module.

Remove from the AngularJS Module

First, let’s open up our AngularJS module (app.module.ajs.ts). You can remove line 22:

import CustomerService from './customers/customerService';

…as well as line 41:

.service('customerService', CustomerService)

That’s it!

Move to Angular Module

Now let’s add our service to our NgModule in app.module.ts so that our Angular code can access it. The first thing we need to do is import the service after line seven:

import { CustomerService } from './customers/customer.service';

Now to register our customer service in our application, we need to add an array of providers to our NgModule after our entryComponents array and add our CustomerService there:

//app.module.ts
providers: [
        CustomerService
    ]

The providers array is where we’ll register all of our services in the application. And now we’ve got our customer service registered in our NgModule and ready to go!

A Quick Note on AOT Compiling

This method of downgrading — registering the downgraded service in the service file and removing it from the AngularJS module file — works perfectly well for development or if you plan on quickly rewriting your application before you deploy. However, the Angular AOT compiler for production won’t work with this method. Instead, it wants all of our downgraded registrations in the AngularJS module. Here in this sample project, we don’t need to worry about that, but I’d be remiss if I didn’t mention it for use in the real world.

The downgrade is identical, but instead you’d:

  • Import downgradeInjectable in app.module.ajs.ts (you’ve already got angular in there so you don’t need to declare it).
  • Change the import of CustomerService to import { CustomerService } from './customers/customer.service'; since we switched to a named export.
  • Change the service registration to the exact same factory registration shown above.

Let’s Make Sure This Works

We’d better make sure our application is still running. Let’s start our Express API, then run our Webpack development server. Open a terminal and run these commands to start Express:

cd server
npm start

Then open another terminal and run these commands to start Webpack:

cd public
npm run dev

You should see everything compile and bundle correctly.

Now, open a browser and head over to localhost:9000. Let’s navigate to our customers route and see if the service is working:

We can double-check that we’re using the rewritten Angular service by going to the sources tab in the Chrome developer tools, navigating down to the customers folder, and clicking on the CustomerService source:

Yep, there it is – our rewritten service!

That’s pretty cool, because we’ve updated the service to Angular, but it’s being used in both the customers component and the customer table component, both of which are still in AngularJS!

GetCustomers As Observable

Now that we’ve got the CustomerService downgraded and working, let’s have some fun and use that getCustomers call as an observable. That way we can start taking advantage of all the new features of observables. This is going to be a little bit tricky, because we’re using the call in both the customers component and the orders component, neither of which have been rewritten to Angular yet. Don’t worry – I’ll show you step-by-step how to do this.

Back in the customer service code, the first thing that we need to do is change the return type on line 16 to Observable. Of course now, TypeScript is complaining to us because we’re converting toPromise, so we just need to delete both the toPromise and then functions. Super easy! It looks like this now:

getCustomers():Observable<any> {
      return this.http.get('/api/customers');
}

Now we need to update our customers component to use an observable instead of a promise. We’ll do that next.

Using an Observable in the Customers Component

Our getCustomers call is now returning on observable. Let’s update our customers component (customers.ts) to use an observable instead of a promise. The customers component is still an AngularJS component and that’s fine, we don’t need to mess with it yet, but let’s use a little TypeScript to help us out. Let’s import our CustomerService at the very top of our file:

import { CustomerService } from './customer.service';

Now that we’ve imported the CustomerService, we can specify the type of our injected CustomerService in our controller function definition:

//customers.ts
function customersComponentController(customerService: CustomerService){

We now have the advantage of TypeScript complaining about our .then just like it did in our CustomerService. It knows that the getCustomers call is supposed to return an observable and that .then doesn’t exist on an observable.

The way we use an observable in a component, whether it’s an AngularJS or Angular component, is to subscribe to it. Before we can subscribe to this observable, we need to import Observable just like we did in the service. So, above our CustomerService import, we’ll add:

import { Observable } from 'rxjs/observable';

This will let us use all the basic functions on observable, including subscribe. So, now on line 18 inside of our $onInit function, we can just change the then to subscribe, and everything else can stay the same.

Let’s go look at the browser and see if this actually worked. If you head over to the customers route, you should see that everything is working the same. However, if we go over to the Orders tab, we see a big problem: no data and TypeError: Cannot read property 'fullName' of undefined in the console. What’s going on here?

It turns out the orders component also uses the getCustomers call, but it’s still trying to use it as a promise. Let’s fix that.

Fix the Orders Component

When we rewrote our getCustomers call to be an observable instead of a promise, we accidentally broke our orders component (orders/orders.ts), which is still in AngularJS. That’s because in our $onInit function, we’re using $q.all to wait for two promises to return before we assign any of the data to our view model:

vm.$onInit = function() {
        let promises = [orderService.getOrders(), customerService.getCustomers()];
        return $q.all(promises).then((data) => {
            vm.orders = data[0];
            vm.customers = data[1];
            vm.orders.forEach(function (order) {
                var customer = _.find(vm.customers, function (customer) {
                    return order.customerId === customer.id;
                });
                order.customerName = customer.fullName;
            });
        });
    };

This was a really common pattern in AngularJS.

The most straightforward solution to this problem would be to just rewrite the orders component to Angular, and also rewrite the order service. But, in the real world, that’s just not always possible right away. Remember, in any large-scale refactoring, the first priority is to make sure we minimize down time and be able to have a continuously deliverable application that we can always deploy to production.

So, despite the fact that the orders component is pretty simple, let’s imagine that this is actually a very complicated piece of our application, or a very mission-critical piece of our application, and we just don’t have time to rewrite it. In that case, we have two choices: we can either convert our getCustomers call to a promise in the orders component, or we can convert the getOrders promise to an observable.

To convert getCustomers to a promise in the component, we’d just do exactly the same thing we did earlier in the service – import Observable from RxJS and add the toPromise operator after getCustomers. It’s that easy, and it’s a super handy trick if you just can’t don’t have time to refactor this component to use observables quite yet. However, it’s not completely desirable, as our long-range goal is to completely get rid of promises and switch entirely to observables. So, Iet’s learn how to convert our getOrders call to an observable here.

Convert getCustomers to a Promise

Let’s convert the getOrders to an observable. The first thing we’re going to do is import our CustomerService at the top of the file just like we did in the customer component:

import { CustomerService } from '../customers/customer.service';

Then we can specify the type of our injected CustomerService in our controller function definition:

//orders.ts
function ordersComponentController(orderService, customerService: CustomerService, $q) {

In order to convert the getOrders call to observable, we’re going to use two static methods on observable called fromPromise and forkJoin. The fromPromise method lets us convert a promise to an observable, and forkJoin lets us subscribe to multiple observables. So, you might have guessed by now that the first thing we need to do is import those two methods at the top of our file:

import { fromPromise } from 'rxjs/observable/fromPromise';
import { forkJoin } from 'rxjs/observable/forkJoin';.

Now we can do some work in our $onInit function. Above line 21, let’s to declare a variable called ordersData and use the fromPromise method:

let ordersData = fromPromise(orderService.getOrders());

Now let re-write $q.all to use forkJoin instead. So, first we’ll just replace return $q.all with forkJoin. We need to pass in an array, so let’s move the promises array and add ordersData to the front of it and then just get rid of the promises declaration. Lastly, let’s change .then to .subscribe just as with a single observable. Here’s our finished $onInit function:

vm.$onInit = function() {
        let ordersData = fromPromise(orderService.getOrders());
        forkJoin([ordersData, customerService.getCustomers()]).subscribe((data) => {
            vm.orders = data[0];
            vm.customers = data[1];
            vm.orders.forEach(function (order) {
                var customer = _.find(vm.customers, function (customer) {
                    return order.customerId === customer.id;
                });
                order.customerName = customer.fullName;
            });
        });
    };

Let’s recap what we’ve done here. First, we called fromPromise and converted our getOrders call from a promise to an observable. Then, we used forkJoin to subscribe to both the ordersData and the getCustomers call. Just like with $q.all, the subscribe for forkJoin will return an array of our data in the order that we’ve listed them. So, data[0] will be our order, and data[1] will be our customers.

Let’s do one more thing to clean this up. We can remove the $q dependency from line 16 in our $inject array and line 167 in our function definition.

Is it working?

Let’s go look at the browser one more time and make sure this worked. You should see that our application compiles and loads correctly, so check out the orders tab:

There it is! Our data is loading correctly! This is great, because now you’ve seen how to translate back and forth between promises and observables, which is really useful when you’re working on a large application where you can’t just convert everything to observables all at once as you’re upgrading.

Where to Go Next

From here, use this guide and the last one to convert the customersTable component and the products route. You’ll need to learn a few new tricks with Angular’s template syntax, but otherwise you’ll have everything you need.

If you liked this guide, I’ve got 200+ detailed videos, quiz questions, and more for you in my comprehensive course Upgrading AngularJS. I created it for everyday, normal developers and it’s the best ngUpgrade resource on the planet. Head over and sign up for our email list to get a free Upgrade Roadmap Checklist so you don’t lose track of your upgrade prep. And, while you’re there, check out our full demo. You’re gonna love it!

See you next time, Scotchers!

Source:: scotch.io