Front-End Web & Mobile

Integrating Existing Applications Into DevOps with AWS Amplify

Many customers are interested in DevOps, or more specifically how to make small incremental changes to their applications and have that small change rapidly deployed, through stages of test and verification, into production. These changes could be either:

  • Application code changes
  • Changes to the supporting infrastructure the code runs on
  • Integrated resource the code relies on, such as API endpoints
  • Changes to underlying data schemas
  • All of the above.

The management of all of the application requirements can be complex. And the way from point A) change, to point Z) production, without multiple cumbersome manual interactions within each environment to support the change, is not always clear. This will typically depend on the type of change or the resources required to support the application.

This article will take a brief look at the advantages of and how to integrate existing AWS resources into AWS Amplify, and the benefits AWS Amplify provides in improving the speed of which changes are deployed from developers code into the hands of the customer.

Why integrating with AWS Amplify supports DevOps

The evolution of modern mobile and front-end development patterns have all moved towards the same objective. To abstract front-end user interface (UI) from back-end logic and infrastructure. However, even with the abstraction, the back-end still has dependencies on resources and infrastructure that are commonly managed by the IT operations team.

In most cases, these resources are procured, setup, and provided to the developers as resource endpoints specific to an environment.

Best case scenario, a developer has direct access to the logic of these resources that can be modified, while requiring a request to be submitted for an update to be deployed.

Worst case, a developer has no visibility of the logic of these resources and only the data contracts or schema to understand what the resource endpoints provide.

In this worst case scenario, a change request to the manager of the resources is required to make a development change, which then needs to be released into the required environment, before then proceeding through subsequent environments in line with the organizations change management process based on the environment constraints and adopted release strategy.

This is in part what DevOps culture attempts to solve through singular directional flow – downstream, and creating, shortening and amplifying feedback loops – bringing agility to development and IT operations. This is managed through the tools and automated pipelines used for the development of application logic and Infrastructure as code.

This is where AWS Amplify goes one step further, by following a code first approach. That is, resources are created locally as code / schemas / templates, that can then be mocked for local development prototyping, and pushed into specified environments.

The developer has direct access to the resource code, schemas, and templates, putting the power in their hands to push changes into the required environment. Reducing the time it takes to deploy changes by minimising the people who need hands on through the change. All while assuring history and versioning of each change is stored in the application repository.

AWS Amplify not only provisions and manages the resources and environments that are required, but also generates code for the application integration based on the developers’ requirements. Using the same code first approach.

Coupling the AWS Amplify back-end environment with the application front-end repository branch further allows the committing of application code to trigger a Continuous Deployment pipeline.

This takes care of everything from the provisioning of the build environment with language and package requirements, to cloning and building both the front-end application code and the back-end resource templates and schemas – taking on much of the undifferentiated heavy lifting common to an application back-end, such as the integration and authentication between resources and management of resource requirements, which traditionally require administration to create and manage.

Integration existing applications, a common approach

So far we’ve looked at the advantages of building applications with AWS Amplify to support a DevOps approach and take on much of the IT Operations heavy lifting, however we’ve not yet spoken to the advantage of integrating existing applications with AWS amplify.

While AWS Amplify provides the UI Components for cloud-connected workflows, open source code libraries and the CLI toolchain, these libraries can be used together or on their own, allowing integration with AWS services at the code level, without having to modify the UI of the existing application meaning that integrating an existing front-end with Amplify can be done in just a few lines of code.

There are a few different approaches to integrating an existing front-end with AWS Amplify. There is the obvious complete back-end rebuild, whereby using the Amplify toolchain to recreate resources. While either connecting with the existing data store, or having AWS Amplify provision a new data store containing migrated data.

This would provide some great benefits in terms of having the resources, functions and data store all managed through AWS Amplify, however, it might not be the most practical approach.

The following diagram represents an existing application with a REST API.

Diagram 1: Bespoke Application

The diagram demonstrates the division of Application from back-end, and also the logical separation of UI and logic, following a common development pattern.

The Amplify client libraries enable you to extend an existing AWS back-end and then integrate with additional back-end resources created by the Amplify CLI:

Diagram 2: Bespoke App with AWS Amplify code libraries

For more on getting started with AWS Amplify, see the guide to getting started here.

The following code sample demonstrates how to add an existing REST API into the configuration of Amplify, in a React app in index.js.

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import Amplify from "aws-amplify";
import { history } from './store/history';
import storeConfig from './store/storeConfig';

Amplify.configure({
  API: {
    endpoints: [
      {
        name: "blog",
        endpoint:
          "https://EXAMPLEKEY.execute-api.ap-southeast-2.amazonaws.com/blog",
      },
    ],
  },
});

ReactDOM.render(
  <Provider store={storeConfig(history)}>
    <ConnectedRouter history={history}>
      <App />
    </ConnectedRouter>
  </Provider>,
  document.getElementById('root')
);

Coupled with the next code sample using Amplify’s REST API library to query the “blog” API added into the configuration. The two samples together demonstrate how only a few lines of code allow the integration of existing resources with existing application code, to integrate into AWS Amplify.

import { API } from "aws-amplify";

// ----------------
// BOUND ACTION CREATORS
export const blogActions = {
    getArticles: () => {
        return async dispatch => {
            dispatch(request());
            
            await API.get("blog", "/article")
            .then(data => {
                const {items, nextToken} = data;
                dispatch(success(items, nextToken));
            }).catch(res => {
                dispatch(failure(res));
            });
        }

        function request() {return { type: actionTypes.ARTICLE_LIST_REQUEST }}
        function success(articles, nextToken) {return { type: actionTypes.ARTICLE_LIST_SUCCESS, articles, nextToken }}
        function failure(error) {return { type: actionTypes.ARTICLE_LIST_FAILURE, error }}
    }
}

At this point, we have configured Amplify to use an existing REST API built with Amazon API Gateway and enabled the app to optionally add additional  features, such as hosting, content distribution and DevOps.

The second step is where the true benefit of integrating with AWS Amplify is realized, as new resources are created and managed through the AWS Amplify CLI Toolchain, and front-end code is generated for the application, which is accessed through the AWS Amplify code libraries.

For example extending the application by provisioning back-end resources managed through the code first approach (CloudFormation Templates and API Schemas) and integrating with the front-end through generated code.

The following shows how to use the AWS Amplify CLI to provision an endpoint to work alongside the existing REST API, using an AWS Amplify GraphQL API (under the hood using AWS AppSync and Amazon DynamoDB).

amplify add api

? Please select from one of the below mentioned services: GraphQL
? Provide API name: myapi
? Choose the default authorization type for the API: API Key
? Enter a description for the API key: demo
? After how many days from now the API key should expire: 7 (or your preferred expiration)
? Do you want to configure advanced settings for the GraphQL API: No
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: Single object with fields
? Do you want to edit the schema now? Yes

This example has generated a GraphQL schema in the backend subfolder of the amplify folder in the application’s source directory, created when Amplify was initialized.

The CLI then creates the following Todo model:

type Todo @model {
  id: ID!
  name: String!
  description: String
}

For our app, we’ll replace the Todo type with a Comment type:

type Comment @model 
  @key(name: "byArticleId", fields: ["articleId", "id"], queryField: "commentsByArticleId") 
{
  id: ID!
  articleId: String!
  name: String!
  comment: String
}

Notice also that we have added an @key directive for a custom index name of ByArticleId so that we can query comments by the article Id. The @model directive builds out the DynamoDB table, CRUD + List operations, and GraphQL resolvers.

For more detail on using GraphQL see the GraphQL Transform Documentation.

To deploy the API we can run the push command:

amplify push

? Are you sure you want to continue? Y
? Do you want to generate code for your newly created GraphQL API? Y
? Choose the code generation language target: javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions: src/graphql/**/*.js
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions? Y
? Enter maximum statement depth [increase from default if your schema is deeply nested]: 2

All that’s left to do is to add the auto generated configuration to App.js above the configuration of the existing API we added in the first step to configure the application to use the newly generated resources.

import awsconfig from './aws-exports';

Amplify.configure(awsconfig);
Amplify.configure({  
    API: { /* Custom API config covered previously */}
)}

Now the GraphQL API provisioned and managed through AWS Amplify can be used alongside the existing bespoke API with only a little code to use both the AWS Amplify API library and the AWS Amplify auto generated code for the back-end resource integration.

import { API, graphqlOperation } from 'aws-amplify';
import * as queries from './graphql/queries';

class ArticleSvc {
    static list = async () => {
        return API.get("blog", "/article");
    }

    static get = async (id) => {
        try {
            const params = { 'queryStringParameters': { 'articleId': id }};
            var data = await API.get("blog", "/article", params);

            if(data.body){
                var response = await API.graphql(graphqlOperation(queries.commentsByArticleId, {articleId: id}));

                return {
                    article: data.body,
                    comments: response.data.commentsByArticleId.comments.items
                }
            }
        } catch (error){
            //handle error codes and raise friendly error
            throw new Error("Get article error")
        }
    }
}

Using the AWS Amplify CLI Toolchain, code first approach, reduces the dependency on IT operations management, freeing up developers and DevOps engineers to focus on more important, value add tasks, that inherently improve the design of the applications itself.

To complete the concept, the application now has both bespoke resources and AWS Amplify managed resources.

Diagram 3: Bespoke App with AWS Amplify managed resources

It is at this point, where the application developers are focusing on continuous improvement of the application, as well as provisioning all new resources through AWS Amplify, and being able to make changes to both application and back-end resources that can be pushed through to deployment directly from the developer.

The reduced amount of effort spent on pushing incremental changes frees up development and operations to allow the migration of existing resources from the first step into AWS Amplify managed resources.

The concept discussed in this article demonstrates how to enable existing applications to migrate from infrastructure dependent to serverless on AWS, following a code first approach, with AWS Amplify – giving power to developers to make and deploy rapid incremental changes into the hands of the users, without the constraints of time spent pushing changes through often manual operations processes.

About the author

Aaron Sempf is a Senior Partner Solutions Architect, in the Global Systems Integrators team. When not working with AWS GSI partners, he can be found coding prototypes for autonomous robots, IoT devices, and distributed solutions.