AWS for Industries

How to Integrate Your Headless Commerce Retail Store With Commercetools – Part 1

In recent years, many retailers have adopted new, modern ecommerce strategies as a point of differentiation by creating unique shopping experiences that reduce friction and increase customer engagement. Headless commerce is one approach that has gained a lot of traction because the storefront technology is decoupled from the backend commerce functionality. With headless commerce, you implement common backend retail business logic as cloud-based microservices, and you connect the backend microservices with different storefront user interfaces (UIs), such as a retail store point-of-sale system, an ecommerce website, or a mobile app interface.

Headless commerce solutions offer several unique benefits:

  • unified user experience—The common backend functionality for retail operations, such as pricing, promotions, and order history, creates a consistent customer experience across all sales channels.
  • customization and personalization—Because front-end and backend functionality are decoupled, headless commerce allows retailers to create customized designs and layouts for different front-end UIs.
  • scalability—With common backend functionality across different sales channels, retailers can spend more time creating the front-end customer experiences that they want to represent their brands and scale the functionality globally.
  • experimentation and innovation—The decoupled architecture of headless commerce means developers don’t have to edit front-end and backend code at the same time. Therefore, developers can test new ideas and functionalities more rapidly.

Headless commerce is a principle of modern MACH (microservices, API-first, cloud-native, headless) architectures. Amazon Web Services (AWS) recently joined the MACH Alliance to facilitate innovation and support enterprises, retailers, and consumer packaged goods (CPG) companies that want to implement modern architectures to deliver better customer experiences.

commercetools_AWS-Blog_thumbnailHeadless commerce with commercetools on AWS

commercetools, an AWS Partner, offers a leading commerce solution built on MACH principles that uses the AWS cloud to provide customers with tools to build high-quality, scalable, modern ecommerce architectures. As a production-ready solution, commercetools is trusted by some of the largest ecommerce brands in the retail, grocery, fashion, and other industries.

commercetools provides a set of flexible REST and GraphQL APIs designed to meet the demanding needs of modern ecommerce solutions. The APIs create an interface for programmatic access to data stored on its solution and associated functionality.

Developers can take advantage of the commercetools solution by:

  • Integrating the solution with AWS artificial intelligence (AI) and machine learning (ML) services, such as Amazon Personalize, a service that allows you to create near-real-time personalized user experiences faster at scale, and Amazon Forecast, an ML-based time-series forecasting service built for business metrics analysis.
  • Initiating event-based processes with Amazon EventBridge, a serverless event bus that makes it easier to build event-driven applications at scale across AWS, existing systems, or software-as-a-service (SaaS) applications, and Amazon Simple Notification Service (Amazon SNS), a fully managed messaging service for both application-to-application and application-to-person communications.
  • Building extensible web and mobile interfaces with AWS Amplify, a set of purpose-built tools and features that allow developers to build extensible, full-stack web and mobile apps faster.
  • Adding unique business logic by injecting functions from AWS Lambda, a serverless, event-driven compute service that lets you run code for virtually any type of application or backend service without provisioning or managing servers, through commercetools’s API extensions.

For the purpose of this blog, I’ll use the AWS Retail Demo Store as the front-end interface for an ecommerce store, and I’ll walk you through the process to implement the orders management functionality, calling the commercetools APIs using the JavaScript software development kit (SDK). Please note that the AWS Retail Demo Store is intended for educational purposes only and not for production use.

Implementing the orders microservice

The orders microservice in the AWS Retail Demo Store is an interface to create and retrieve orders. The web UI makes calls to this service when shoppers view their orders or complete the checkout process. The web UI expects the orders microservice to provide the following interface:

orders microservice interface

Before you can begin an implementation effort, you will need to create an account with commercetools. If you want to test the service, you can sign up for a 60-day free trial. We recommend following the steps in the “Getting started with commercetools” tutorial to set up an API client. We will use the client in the implementation process.

First, you should create a client to invoke the commercetools APIs. When you create an API client from the commercetools Merchant Center, you can download the client credentials in different formats, depending on which SDK or tool you plan to use. The code below uses the JavaScript SDK.

const {
  ClientBuilder,
  createAuthForClientCredentialsFlow,
  createHttpClient,
} = require('@commercetools/sdk-client-v2')
const { createApiBuilderFromCtpClient } = require('@commercetools/platform-sdk')
const fetch = require('node-fetch')

const HTTP_OK = 200;

const projectKey = 'YOUR_PROJECT_KEY';

const authMiddlewareOptions = {
  host: 'https://auth.eu-central-1.aws.commercetools.com',
  projectKey,
  credentials: {
    clientId: 'API_CLIENT_ID',
    clientSecret: 'API_CLIENT_SECRET',
  },
  scopes: ['API_CLIENT_SCOPES'],
  fetch,
}

const httpMiddlewareOptions = {
  host: 'https://api.eu-central-1.aws.commercetools.com/',
  fetch,
}

const client = new ClientBuilder()
  .withProjectKey(projectKey)
  .withMiddleware(createAuthForClientCredentialsFlow(authMiddlewareOptions))
  .withMiddleware(createHttpClient(httpMiddlewareOptions))
  .withUserAgentMiddleware()
  .build()

const apiRoot = createApiBuilderFromCtpClient(client)

You can implement different methods to retrieve, create, and update orders using the commercetools APIs.

export module ctClient {

  export async function getOrders() {
    let result: any[] = [];
    await apiRoot
      .withProjectKey({ projectKey })
      .orders()
      .get()
      .execute()
      .then((res: any) => {
        if (res.statusCode == HTTP_OK) {
          result = res.body.results;    
        }
      })
    return result;
  }

  export async function getOrdersById(orderId: string) {
    let result: any[] = [];
    await apiRoot
      .withProjectKey({ projectKey })
      .orders()
      .withOrderNumber(orderId)
      .get()
      .execute()
      .then((res: any) => {
        if (res.statusCode == HTTP_OK) {
          result = res.body.results;    
        }
      })
    return result;
  }

  export async function getOrdersByUsername(userName: string) {
    let result: any[] = [];
    await apiRoot
      .withProjectKey({ projectKey })
      .orders()
      .search()
      .post({
        body: { query: '{ "filter": [ { "exact": {  "field": "createdBy.clientId", "value": ' + userName + ' } } ] }' }
      })
      .execute()
      .then((res: any) => {
        if (res.statusCode == HTTP_OK) {
          result = res.body.results;    
        }
      })
    return result;
  }

  export async function createOrder(order: string) {
    let result: any[] = [];
    await apiRoot
      .withProjectKey({ projectKey })
      .orders()
      .post({
        body: JSON.parse(order),
      })
      .execute()
      .then((res: any) => {
        if (res.statusCode == HTTP_OK) {
          result = res.body.results;    
        }
      })
    return result;
  }

  export async function updateOrder(orderId: string, orderUpdate: string) {
    let result: any[] = [];
    await apiRoot
      .withProjectKey({ projectKey })
      .orders()
      .withOrderNumber(orderId)
      .post({
        body: JSON.parse(orderUpdate),
      })
      .execute()
      .then((res: any) => {
        if (res.statusCode == HTTP_OK) {
          result = res.body.results;    
        }
      })
    return result;
  }
}

Next, create a wrapper service for the commercetools API client that implements the AWS Retail Demo Store orders service interface.

import * as bodyParser from "body-parser";
import * as express from "express";
import { Logger } from "../logger/logger";
import { ctClient } from "./commercetools-client";

class OrderRoutes {

    public express: express.Application;
    public logger: Logger;

    // array to hold users
    public orders: Order[];

    constructor() {
        this.express = express();
        this.middleware();
        this.routes();
        this.logger = new Logger();
    }

    // Configure Express middleware.
    private middleware(): void {
        this.express.use(bodyParser.json());
        this.express.use(bodyParser.urlencoded({ extended: false }));
    }

    private routes(): void {

        // request to get all the orders
        this.express.get("/all", async (req, res) => {
            this.logger.info("url:::::::" + req.url);
            let result = await ctClient.getOrders();
            res.json(this.prepareResponse(result));
        });

        // request to get an order by orderId
        this.express.get("/id/:orderId", async (req, res) => {
            this.logger.info("url:::::::" + req.url);
            let result = await ctClient.getOrdersById(req.params.orderId);
            res.json(this.prepareResponse(result));
        });

        // request to get all the orders by userName
        this.express.get("/username/:userName", async (req, res) => {
            this.logger.info("url:::::::" + req.url);
            let result = await ctClient.getOrdersByUsername(req.params.userName);
            res.json(this.prepareResponse(result));
        });

        // request to create the order
        this.express.post("/", async (req, res) => {
            this.logger.info("url:::::::" + req.url + ":::::::body::::::" + JSON.stringify(req.body));
            let result = await ctClient.createOrder(this.prepareCreateRequest(JSON.stringify(req.body)));
            res.json(this.prepareResponse(result));
        });

        // request to create the order
        this.express.options("/", async (req, res) => {
            this.logger.info("url:::::::" + req.url + ":::::::body::::::" + JSON.stringify(req.body));
            let result = await ctClient.createOrder(this.prepareCreateRequest(JSON.stringify(req.body)));
            res.json(this.prepareResponse(result));
        });

        // request to update the order
        this.express.put("/id/:orderId", async (req, res) => {
            this.logger.info("url:::::::" + req.url + ":::::::body::::::" + JSON.stringify(req.body));
            let result = await ctClient.updateOrder(req.params.orderId, this.prepareUpdateRequest(req.body));
            res.json(this.prepareResponse(result));
        });

        // request to update the order
        this.express.options("/id/:orderId", async (req, res) => {
            this.logger.info("url:::::::" + req.url + ":::::::body::::::" + JSON.stringify(req.body));
            let result = await ctClient.updateOrder(req.params.orderId, this.prepareUpdateRequest(req.body));
            res.json(this.prepareResponse(result));
        });
    }
    
    // OMITS: request and response adapter methods
    [...]
}
export default new Order().express;

Once you’ve implemented the methods in the wrapper service, you can deploy the new orders microservice to the AWS Retail Demo Store; shoppers can place orders, and the orders will appear in the commercetools Merchant Center for processing. You can implement additional retail functionality, such as shopping carts and products, in a similar way.

Start your headless commerce integration project with commercetools today

In this post, we showed you how to integrate the commercetools solution with your ecommerce front end. Read How to Integrate Your Headless Commerce Retail Store With Commercetools—Part 2, where we will walk you through the process to react to events generated by your commercetools backend with the Amazon EventBridge integration.

If you want to discuss how a headless commerce architecture can benefit your retail business or you need help integrating commercetools with your existing headless commerce infrastructure, contact your AWS account team or visit Retail on AWS today to get started.


AWS Partner Spotlight

commercetools is a leading commerce solution built on modern MACH principles (microservice-based, API-first, cloud-native, headless), allowing retailers to work with, not around, their commerce solution to tailor experiences to the exact needs of the business and its customers.

Learn more about commercetools here.