5/15/21

React Static Web Apps Manage ChunkLoadErrors on Dynamic Routes

The React JavaScript framework for Single Page Applications (SPA), which can be hosted on Azure Static Web Apps (SWA) or CDN hosting, supports the concept of Code Splitting by loading pages/routes dynamically instead of building one single package.  The benefit of Code Splitting is that it enables for faster load time on the user’s browser as opposed to loading the entire application on one single request. This feature however also introduces other concerns that we need to manage with code otherwise, the user can end up with a white page in front of them as the application is unable to render the requested content.

ozkary chunk load error


To leverage Code Splitting, we load the page routes by using React Lazy* Loading and Dynamic Import features. This way the page, or chunk, for the selected route is loaded only when it is requested. Because this is done a run-time, a new request is made to the server to download the chunk of code that is needed to render the page. Yes, this is a server-side trip to get the additional resources, and the application still works as an SPA.

Note:  Lazy loading is an application architecture that is used to load code in memory or web pages only when it is needed. This improves application performance and load time.

Because a server-side request must be made to download an additional chuck of code and render the route or page properly, there could be failures that are reflected as the following errors:


Uncaught ChunkLoadError: Loading chunk 2 failed.(timeout)


The failure could be due to two main reasons:

  •         There is a network latency issue and the content failed to download
  •          The client application may have been cached on the browser and App update has replaced those files with new hash codes

For the network problem, we can add some error handling and retry logic to allow the application to get the chunk again. We do need to be careful here and avoid locking the user on some retry logic because the second case, app update, can be happening, in which case the only way to solve the issue is by refreshing the entire app again.  Let’s look at the code below and talk about the root cause of this issue.

 

Ozkary - React Dynamic Routes

After looking at the code, we can see that we are lazy loading dynamic imports by using promises.  Those directives tell the compiler to create a chunk for that route, and when that route is dynamically requested by the user, the chunk is downloaded to the browser. The issue with this code is that there is no error handling, and promises can fail to download the chunk resulting on the ChunkLoadError.

To address this issue, we create a loader component that can manage both the error and attempts to download the requested chunk. At the same time, this component needs to be able to limit the number of retries, to avoid an infinite loop, and decide to load the entire app again. Let’s look at a simple implementation on how that could be done.

Loader Component


ozkary route loader component


Using the Loader Component 

 

ozkary load routes with error handling


After looking at our solution, we can see that we are lazy loading the loader component which manages the promises, errors and retry activities. The component uses a default limit for the number of attempts that should try to download the next route. This is done by calling the same function recursively and decreasing the limit with every attempt until the limit is decrease to zero.  When the limit is reached, it does the next best thing, which is to reload the application.  If the chunk files for the current version of the application are still available, the retry logic should be able to solve the problem. Otherwise, a page reload takes place to download the application with the updated chunk information.

Code Gists

Route Loader Gist

Routes Gist

Conclusion

For this simple implementation, we decided to reload the application when a retry continues to fail. Depending on the use-case, the approach can be different. For example, a nice message can be displayed to the user explaining that a new update is available, and the application needs to be updated. This is the best user experience as feedback is provided to the user.

An important concern to consider when using Code Splitting is that a ChuckLoadError can take place for users with network issues or when a new update is pushed to production. Therefore, additional design and architecture considerations must be thought of before just adding the code splitting performance improvement to a React single page application.

Thanks for reading.

Originally published by ozkary.com

4/17/21

Modernize SOAP Web Service with Azure API Management

Simple Object Access Protocol (SOAP) is an XML based protocol standard to build Web Services. It provides a communication channel for data exchange between applications that are built with different technologies. As a result, most companies have invested heavily in building SOAP Web Services.

SOAP, however, has not been the de facto approach for building modern Web application for some time now.  With the creation of new protocols and standards, REST (Representational State Transfer) and JSON (JavaScript Object Notation) are now widely used for new projects, leaving SOAP services mostly on maintenance or support mode only.

ozkary-apim-gateway-soap


SOAP began to lose momentum because of its limitations. The XML message format is a verbose compared to the JSON format. SOAP is also tightly coupled as the client-server communication uses a strict contract definition, WSDL (Web Service Description Language). RESTful APIs in contrast provide an architectural style with loose guidelines and supports several message formats like plain text, XML, JSON, HTML.

Replacing a SOAP Service

The decision to replace a SOAP service with other technologies can be a challenging one. Depending on how complex the project is, it may just be a tremendous investment full of risks for any company. In addition, there are the operation cost to support the new technology and still support clients that cannot migrate to the new API definition.

Some companies may also have the argument that their APIs are doing a great job, and they do not need to be updated with new technology.  For some clients, however, it may be a risk to take on an integration investment with a company that supports, what many perceive as, older technology, or that there are security limitations. Luckily for situations like this, there are alternatives that can meet both sides of the argument.

Modernize a SOAP API

With the growth of cloud technology, there are ways to modernize a legacy API without having to re-write the API which immediately becomes very appealing as this effort saves companies with project management and implementation cost.  For a solution like this, we need a system that can provide a reverse-proxy and document format mapping capabilities.  Azure cloud provides the perfect solution with the API Management service offering.

API Management (APIM) Service

Azure APIM service accelerates this kind of solutions by providing multiple capabilities which enables the managing of APIs by protecting the resources with authentication, authorization, IP Whitelisting, and usage limits. It also provides reverse-proxy capabilities which enable companies to publish REST-based APIs by creating Open API endpoints which act as façade for the legacy systems. It supports the transformation of XML documents into JSON documents and Vice-Versa which makes it the obvious solution for modernizing a legacy SOAP API.

Modernization Process

The process to modernize a legacy SOAP API is much simpler and takes lot of less effort than to take on a rewriting effort. This is mostly due to the metadata reading capabilities of the APIM service. The process starts by importing a WSDL document into the service which reads the metadata to create facades with mapping policies for inbound and outbound stages of the request lifecycle.  The façade is the Open API definition of the RESTful service. The mapping policies enable the mapping and transformation of JSON to XML documents for inbound processing as well as XML to JSON transformation for outbound processing. The Inbound stage handles the JSON payload request which needs to be transformed into XML, so it can be proxied to the legacy SOAP API.  The outbound stage handles the XML response from the legacy SOAP service, and it transforms the response into a JSON document, so the client application can consume it.

Sample Process

Now that there is a good conceptual understanding of the approach, let’s look at a practical example by using a publicly available SOAP API which returns a list of continents by name.  Note that any other SOAP API can be used instead as the process is really the same. Let’s review the SOAP API specifications for this Web service.

Service endpoint:

http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso

Open or use any tool like Postman that can enable sending API requests to the service. Send a POST request with this header Content-Type: text/xml.  Also add the following for the request body payload.

 

<?xml version="1.0" encoding="utf-8"?>

<soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">

  <soap12:Body>

    <ListOfContinentsByName xmlns="http://www.oorsprong.org/websamples.countryinfo">

    </ListOfContinentsByName>

  </soap12:Body>

</soap12:Envelope>

 

The expected response should be an XML document that looks as follows:

 

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

    <soap:Body>

        <m:ListOfContinentsByNameResponse xmlns:m="http://www.oorsprong.org/websamples.countryinfo">

            <m:ListOfContinentsByNameResult>

                <m:tContinent>

                    <m:sCode>AF</m:sCode>

                    <m:sName>Africa</m:sName>

                </m:tContinent>

                <m:tContinent>

                    <m:sCode>AN</m:sCode>

                    <m:sName>Antarctica</m:sName>

                </m:tContinent>

                <m:tContinent>

                    <m:sCode>AS</m:sCode>

                    <m:sName>Asia</m:sName>

                </m:tContinent>

                <m:tContinent>

                    <m:sCode>EU</m:sCode>

                    <m:sName>Europe</m:sName>

                </m:tContinent>

                <m:tContinent>

                    <m:sCode>OC</m:sCode>

                    <m:sName>Ocenania</m:sName>

                </m:tContinent>

                <m:tContinent>

                    <m:sCode>AM</m:sCode>

                    <m:sName>The Americas</m:sName>

                </m:tContinent>

            </m:ListOfContinentsByNameResult>

        </m:ListOfContinentsByNameResponse>

    </soap:Body>

</soap:Envelope>

 

On Postman, this configuration with the request and response should look like this:

ozkary-postman-soap-api

This example uses XML envelopes for the request and response. To modernize this API, we can import the WSDL definition of the API into Azure APIM. To get the WSDL file, just add the wsdl parameter to the query string and send a GET request. The response should be an XML schema file which describes the API operations and data types.

http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?wsdl

Import the WSDL

Note: The following steps should be done on a subscription that has an API Management service configured.

To import the API schema definition into APIM, open Azure Portal, API Management console. Click on the API link to open the existent API definitions. From this view, click on Add API and select the WSDL option. Configure the popup information as shown on the image below. Make sure to use the URL that has the wsdl parameter on the query string. When ready, press create to generate the API definition with policies.

Note: Make sure to use the URL that has the wsdl parameter on the query string

ozkary-import-wsdl

A new API with the name of ContryInfoService should get created. Click on that name to display the operations that are available from this API. Check the settings for this API and make a note of the Base URL. This is the URL that should be used to send request to the new API.

 We are interested in finding the ListOfContinentsByName operation. This is the same API that we use for the Postman validation.  Once selected, review the inbound and outbound policies by clicking on the code icon as shown below:

ozkary-apim-policy

Policy File

APIM Policy Gist

The policy has an inbound and outbound node. The inbound node uses a liquid template to transform the incoming payload into a SOAP envelope which is required by the SOAP service.  The outbound node also uses a liquid template to transform the XML response into a JSON payload which is then sent back to the calling application. The markup use on the liquid template has many features, and we could no cover all the details here.  For this simple example, we can see how a new JSON document is created by adding the items from the continents collection.

We should now have a JSON API ready, but we are not yet RESTful. The SOAP service requires documents to be send for every request, as a result it uses POST request method for even reading information. A RESTful service should instead be able to get the continent information using a GET request method instead of using POST which should only be used for creating new resources. We should remove that constraint by using APIM to change that configuration as well. Let’s load the operation settings and click on the Frontend configuration. From this configuration, we can change the request method from POST to GET, and even change the URL route to something that aligns with our naming conventions. Also notice that on the inbound policy, we add the set-method policy to indicate that even if the request came in as a GET request, we want to forward this as a POST request which is what the SOAP API expects. The new setting should look as follows:

ozkary-change-api-operation

All should be setup to make requests to the new API. If no extra configuration was done like adding a subscription key, and the APIM resource was not created in a private subnet, we should be able to access the API from the internet. Let’s load our tool of choice and make a request to the new API. Get the Base URL from the setting and append the operation name. That is the new RESTful route. We can now configure a request with the header Content-Type: application/json and set the method to GET. There is no need to add anything on the body as there is no JSON payload to send during a GET operation. This is what the new configuration should look like.

ozkary-apim-json-response

As we can see the results show a list of continents, but instead of showing an XML document it is now showing the data on a JSON document.

Conclusion

Modernizing a legacy SOAP API can be a very expensive and long project. In cases when it is needed to accelerate the solution and minimize the risks, Azure API Management provides a solid approach. This service provides capabilities to create a REST-Base API, policies to handle document transformation and enhances the security of the APIs. All this can be achieved without having to decommission the existent legacy SOAP API and getting a business to provide modern APIs to its customers.

Originally published by ozkary.com

3/20/21

Application Insights for Single Page Applications

    Application monitoring is a fundamental service that any application should have to understand the performance and health of it. When it comes to Single Page Applications (SPA) written on Angular, React or similar frameworks, monitoring becomes crucial because the application runs on the user’s browser.  Ideally, we want to be able to log errors and track custom events to better understand the application performance without having to code an API which consumes the telemetry information.

ozkary-appsinghts-integration

Application Insights

    Application Insights (AppInsights) is an Application Performance Management (APM) service part of the Azure Monitor services. It is used to monitor running applications.  It is widely used on different platforms like .Net, Node.js, Python and client-side applications written with JavaScript frameworks like React, Angular and others. It is also used by mobile application to help manage crash reporting from all the devices where the application is running from.

    On the cloud, AppInsights provides data analytics tools that are used to understand user behavior and application performance.  The analysis of the data leads to the continuous improvement of the application usability and quality. In addition, visualization tools like PowerBI can be used to tap into the data and create dashboards that can provide insights to the DevOps and Development teams as well as UX designer for usability cases.

How does it work on a SPA?

    At the application level, there is an NPM package that must be installed in the Single Page application to help use the AppInsights services to track events, errors, and API calls. Those events are sent to the Azure monitor service in the cloud via API calls. The API is provided by the monitor service on Azure, and the location of the endpoint is defined in the NPM package.

Note: AppInsights automatically tracks telemetry data like page views, request performance, user sessions and unhandled exceptions for crash reporting. For custom error and event handling, continue to read this article.  

    For the Azure monitor service to resolve the tenant and AppInsights instance information, we need to get the instrumentation key from the application insights instance. This information can be found on the overview blade of the instance associated to your application. To create the instance, visit the resource that is hosting the SPA, click on the Application Insights link and enable the integration. That should provision the AppInsights instance for the application.

Client configuration

    On the client application, open Visual Studio Code and open a terminal console. On the console, install the AppInsights NPM package by entering this command:

 

npm i –save @microsoft/applicationinsights-web

 

 

    Once the package is installed, we can add the instrumentation key to the application as an environment variable. The application environment file (.env) is the right place to add this key. This file uses a key=value format to set those variables.  Most SPAs have an environment file to host application-level variables which can be accessed by using the process object.  If the project does not have this file, one can be created manually. For the variables to be loaded properly, the application needs to be restarted.  Also depending on the current framework and version, like React or Angular, the NPM package env-cmd maybe installed. This package handles the parsing and loading of the variables from the file when the application first loads.

Note: In newer versions of the JavaScript framework, there is no need to add the NPM package.

Note:  Depending on the framework, the variable names should follow a particular naming convention. For example, React requires that the variables are prefixed with the tag REACT_APP

As an example, the instrumentation key should be set with the following format:

 

REACT_APP_INSIGHTSKEY=key-value-here

 

     The variable value can then be accessed from anywhere in the code by using the process object.

 

const key = process.env.INSIGHTSKEY;

 

Build the logger

    After setting the configuration, we can now implement a service where we can centralize the logging of errors and events. Usually, the logger or exception manager service on the application is the right place for that. This way anywhere in the app, we can import the logger and use it to log errors and events to the cloud.  Review a simple implementation of how this service could be implemented using TypeScript.

Logger Gist

    After reviewing the code, we can notice all the steps taken to initialize the service and what is exported for other components to call. The code first imports the ApplicationInsights dependency. It then instantiates the service with the instrumentation key, which is read from the environment variables. The service exports three functions, init, error, track.

    The init function initializes or bootstrap the AppInsights service. This allows it to track and send telemetry information like page visits and performance metrics automatically. The error function is used to send exception and custom error messages. The track function sends custom events. It uses generics for the data parameter, so it can support events with primitive and complex types.

How to log events

    We should now be ready to look at our application to figure out what we need to log.  For example, we could log handled errors and some important telemetry about our app. For telemetry, we can log the navigation, click events and other custom events that are important to track.

    Each component and/or service of the application, should be handling errors and custom events. The custom events should use a tag name and some measure or message that you want to track.  It is important to define this information properly, so it can be shown on the analytic tools hosted on Azure.

Custom errors

    Custom errors are handled exceptions or unexpected results by the application. These errors are usually handled on a try/catch block or some business rule validation. To log the error, just use the logger service and call the error function, passing the Error object as parameter.

Custom Errors Gist

Custom events

    Custom events are the business events that track application critical messages that can be measured as goal or conversion on the application. Some examples may include customer registration, profile changes and other measurable events that should be reported on the analytical tools. Use the track function of the logger service to log events. Send a tag, for grouping, and a JSON document with some information about the event.

Custom Events Gist

Verifying the data

    After deploying the application and running some test that can trigger some errors or custom events, verify that the messages are sent correctly.  The first verification is to use the browser console, network tab.  We should see some traffic with the track request and VisualStudio endpoint. We are looking for the status to be 200 which should indicate that the message made it home.

    Next, head over to the Azure Portal and open the Application Insights associated with the application. There are multiple tools to use there, so experiment and learn from them. A tool to use and query data is available from the Logs link. This tool list all the tables that are available. To query the custom events, use the custom events table and query the data with a SQL like script.

ozkary-appinsights-logs


    For visual statistical analysis, we can use the Events tool. This presents the data on charts, which enables us to visualize the data from the application.

Conclusion

    It is very valuable for our client-side apps to be able to report telemetry information to the cloud. This can enable teams to learn about the application performance and usability. Azure Application Insights provides a solution that enables all applications, built on different platforms, to easily integrate with the service. It is a complete solution because it provides the pipeline to send telemetry data from the applications as well as the analytical tools to view, monitor and learn from the telemetry data.

Thanks for reading.

Originally published by ozkary.com

2/20/21

Azure Static Web App GitHub Actions CICD

Static Web App (SWA) is a software as a service (SaaS) solution hosted by Azure cloud which enables us to use Content Delivery Network (CDN) to host single page applications (SPA) built with JavaScript frameworks like React, Angular and others.

In addition to the provisioning of the SPA, a serverless function endpoint is created as part of the application. This enables the JavaScript code to call APIs within the same domain to avoid Cross-Domain problems. The default route used by the client-side code is set to /api by default.

While this SASS solution manages the hosting concerns for our apps, we still need to be able to manage the DevOps concerns for building and deployment of the application. For that process, we can rely on the use of GitHub Continuous Integration Continuous Delivery (CICD) pipelines to manage the build and deployment process of our applications thus making this architecture a fully automated turn-key solution for any static web application.

ozkary gitub cicd


What are we learning?

In this article, we look at automating the deployment of the Azure resources to create a SWA using the Azure CLI. This way, we have a repeatable process to deploy other apps. We also link our SWA with a GitHub branch, so we can create a GitHub action which builds and deploys our application the moment we merge a pull-request into the branch.

To follow along, make sure to have all the requirements available on your workstation. This includes Azure CLI, Visual Studio Code and a GitHub repository with a sample project and token. The token is used to access the repository with the permissions to create the GitHub Action workflow file and API secrets.

https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token

https://docs.microsoft.com/en-us/cli/azure/install-azure-cli

To get started, we need to open our GitHub Repo with Visual Studio Code (VSC). We then need to open a terminal console right from VSC, so we can enter the CLI commands. When the terminal is ready, we can type the following command to make sure that Azure CLI is correctly installed.

 

az –version

 

 

Azure CLI Commands Reference:

https://docs.microsoft.com/en-us/cli/azure/

This command should return the current version of the Azure CLI. If this is not the case, review the Azure CLI installation from the Microsoft website. If the CLI is correctly installed, we can next look at the following Bash script, so we can understand what information we need to provide to create the SWA and link it to the GitHub action.

Note: Bash script can run from a Linux terminal or Windows using WSL with the Linux subsystem.

Azure CLI Gist

Review the CLI script

This script prompts the user for information to execute a series of commands to create the resources. In general, these are the commands and their purpose:

Command

Notes

az login

Login to Azure


az account show

Shows the current account for verification


az account set

Sets the default subscription which host the resource to be created


az group create

Creates a resource group with location information like East/West


az staticwebapp create

Creates the SWA and GitHub action to automate the deployment after a push is done on the provided branch


az staticwebapp appsettings

Sets configuration settings for the application. These settings are server-side settings which can be used by the serverless functions that are created automatically with our SWA



After reviewing the code, we can download the gist and run it from Visual Studio Code.  To run the Bash file on a terminal window, enter the following command:


bash filename


Follow and enter the information as prompted. Once all the commands are executed, we should do a git pull from the repository branch. The pull downloads the CICD workflow file that was created when the SWA resource was provisioned.

Review the CICD workflow

Once the git pull is complete, we should notice in Visual Studio Code explorer that a new folder (github) and yml file is created.  Open the file, so we can review the commands which the workflow executes to build and deploy our code. From the yml file, we should see the workflow name, branch name, jobs and build and deploy configuration. In general, we should change the workflow name to something meaningful, like the branch or resource name. This name is visible on the GitHub Actions tab, so we want to be able to easily identify what action is associated with a resource.

On the Build and Deploy configuration section of the workflow, we want to review three settings:

Setting

Notes

app_location

This should be set to the location of the source code. This is usually the root folder. Change this to match the project location.

 

api_location

This by default uses the path /api which is the route use to call the serverless functions. This should also be changed to match the project configuration.

 

app_artifact_location

This is set to the build folder location. In the case of React and Angular projects, this folder is usually named build. If this is not the case in the current project, update this to match the project configuration.

 

If this information is not correct, the GitHub action fails with a “Unable to find a default file in the artifact folder”.

 

The error is visible in the Actions' area of GitHub.  There, we can find all the workflow executions every time we push code changes into the repository branch.

 


See this example workflow yml file for more information.

ozkary-github-action

After making changes to the YML file, we can create a PR or push directly the changes to the branch.  This should trigger the action, and we should see our changes work and see how the build is deployed to the Azure resources.  

Once the build is complete, open the Azure console and visit the Static Web App resources. Open the one that was just created and click on the URL, which can be found on the overview tab. If all is well, the site should load on the browser.

Automation for all

There are many options to host your SPA, but it is important to be able to automate the build and deployment process, as well as the provisioning of the resources that need to be created on any cloud platform. Azure Static Web Apps with GitHub Actions provide an excellent process to manage the automation of all our DevOps concerns for our Single Page Apps.

Thanks for reading.

Originally published by ozkary.com