How to build a functional front-end for HCL commerce with Gatsby
by Rúnar Sverrisson, 6/9/2020, 8:36:52 AMSetting up
- Install Node.js: Download and install the latest Node.js version from the official Node.js website
- Install Git
- Install Gatsby CLI: The Gatsby CLI is available via npm and should be installed globally by running
npm install -g gatsby-cli
- Create a Gatsby site:
gatsby new gatsby https://github.com/gatsbyjs/gatsby-starter-hello-world
Now we can do cd gatsby
and then gatsby develop
to start the Gatsby development server. We can access the Gatsby starter site at http://localhost:8000/
. For more info and tutorials on Gatsby, see the excellent Gatsby docs.
Sourcing data and generating pages
- If your private API is a GraphQL API, you can use
gatsby-source-graphql
. - If your private API is not a GraphQL API and you are new to GraphQL, treat the data as unstructured data and fetch it during build time, as described by the guide ”Using Gatsby without GraphQL”. However, as highlighted in the guide, this approach comes with some tradeoffs.
- Create a source plugin, as described in the tutorial ”Source plugin tutorial“
We will describe how we can use option two to source the data we need from the HCL Commerce APIs and create pages for the store. The gatsby-node.js
file is used to source the data and create the PDPs and PLPs:
/** * Implement Gatsby's Node APIs in this file. * * See: https://www.gatsbyjs.org/docs/node-apis/ */ // You can delete this file if you're not using it const axios = require('axios'); const get = endpoint => axios.get(`http://localhost:80/wcs/resources/store/1/productview/byCategory/10001`); const getProductData = categoryIds => Promise.all( categoryIds.map(async id => { const { data: CatalogEntryView } = await get(``); return { ...CatalogEntryView }; }) ); exports.createPages = async ({ actions: { createPage } }) => { // `getProductData` is a function that fetches our data const allCategory = await getProductData(["10001"]); // Create a page that lists all Product. allCategory.forEach(category => { createPage({ path: `/category/10001`, component: require.resolve("./src/templates/category.js"), context: { category }, }) }) // Create a page for each product. allCategory.forEach(category => { category.CatalogEntryView.forEach(product => { createPage({ path: `/category/10001/product/${product.uniqueID}/`, component: require.resolve("./src/templates/product.js"), context: { product }, }) }) }) }
src\templates\
import React from 'react'; import { Link } from 'gatsby'; export default ({ pageContext: { category } }) => ( <div style={{ width: 960, margin: '4rem auto' }}> <h1>Product List Page - PLP</h1> <h1>Total Products : {category.recordSetCount}</h1> <ul style={{ padding: 0 }}> {category.CatalogEntryView.map(product => ( <li key={product.uniqueID} style={{ textAlign: 'center', listStyle: 'none', display: 'inline-block' }} > <Link to={`/category/10001/product/${product.uniqueID}`}> <img src={`https://localhost:8443${product.thumbnail}`} alt={product.partNumber} /> <p>{product.partNumber}</p> </Link> </li> ))} </ul> </div> );
import React from 'react'; import { Link } from 'gatsby'; export default ({ pageContext: { product } }) => ( <div style={{ width: 960, margin: "4rem auto" }}> <h1>{product.uniqueID}</h1> <h1>{product.name}</h1> <h5>{product.shortDescription}</h5> <h3>${product.Price[0].priceValue}</h3> <h1>{product.partNumber}</h1> <img src={`https://localhost:8443${product.thumbnail}`} alt={product.name} /> </div> )
Now, after we build the code we can see all pages the are created under public\page-data
- public\page-data\category\page-data.json
- public\page-data\category\10001\page-data.json
- public\page-data\category\10001\product\10039\page-data.json
We could also have sourced the data by creating a source plug-in and then used graphQL to reference it in gatsby-node.js
. More info on source plug-ins in Gatsby here, but at a high-level a plug-in would do the following:
-
Accept config options like an API key and/or a search query
-
Make an API request (to the HCL Commerce REST API) using the provided config options
-
Convert the data in the API response to Gatsby's node system
Shopping cart
The PDPs and PLPs were generated pages that used data sources and template files to prepare the final HTML. For the shopping cart we will just prepare a single page, and we do that by adding a file src/pages/cart.js
, so no templates are used this time around. We will call the HCL Commerce endpoint that provides details on the shopping cart: http://localhost:80/wcs/resources/store/1/cart/@self?pageSize=50&pageNumber=1&responseFormat=json'
. This will provide us with a JSON that contains everything that is in the cart. We loop through the response to create the shopping cart:
import React, { useState, useEffect } from "react" import { Link } from "gatsby" import Layout from "../components/layout" import SEO from "../components/seo" const axios = require('axios'); const fetch = require(`node-fetch`); const CartPage = () => { const cartURL = 'http://localhost:80/wcs/resources/store/1/cart/@self?pageSize=50&pageNumber=1&responseFormat=json'; // Client-side Runtime Data Fetching const [cartData, setCartData] = useState(0) useEffect(() => { // get data from GitHub api fetch(cartURL, {credentials: "include"}) .then(response => response.json()) // parse JSON from request .then(resultData => { setCartData(resultData) }) }, []) return ( <Layout> <SEO title="Shoping Cart" /> <h1>Shopping cart</h1> <table> <tr> <th>Product</th> <th>QTY</th> <th>Each</th> <th>Total</th> </tr> {cartData.orderItem && cartData.orderItem.map(item => ( <tr> <td><Link to={`/category/10001/product/${item.productId}`}>{item.partNumber}</Link></td> <td>{item.quantity}</td> <td>{item.unitPrice}</td> <td>{item.orderItemPrice}</td> </tr> ))} <tr> <td></td> <td></td> <td>Order Subtotal:</td> <td>{cartData.totalProductPrice}</td> </tr> <tr> <td></td> <td></td> <td>Discount:</td> <td>({cartData.totalAdjustment})</td> </tr> <tr> <td></td> <td></td> <td>Order Total:</td> <td>{cartData.grandTotal}</td> </tr> </table> </Layout> ) } export default CartPage;
It is not enough to have a shopping cart, we need a way to add items to the shopping cart. In order to do that we need add some functionality to the PDPs. To get product details we can use GET /wcs/resources/store/10001/productview/byId/13011
endpoint. Then to add to the cart we use something like
POST /wcs/resources/store/10001/cart?responseFormat=json { "orderId": ".", "orderItem": [ { "productId": "10706", "quantity": "1" } ], "x_calculateOrder": "0", "x_inventoryValidation": "true" }
Checkout
We will need to build a checkout flow for the store as well. That will involve three views: Shipping/Billing page, Payment/Review page and a Confirmation page. We will do that similar to the Shopping Cart page; that is, we will add a new entry to src/pages
for each page. For each page we will also have to call to proper HCL Commerce endpoints to send and receive data. The resulting pages look like this:
Shipping and Billing information[/caption][caption id="attachment_55420" align="aligncenter" width="1920"] Payment and Review[/caption][caption id="attachment_55421" align="aligncenter" width="1920"] Confirmation page[/caption]
Espots and CMC content
If we need to display Espots or other content from the CMC, it is easily accessible via the HCL Commerce REST API. The following table summarizes some of the available endpoints
HTTP Method |
Path |
Description |
---|---|---|
GET |
Retrieve a list of all search terms that have search rules. |
|
GET |
Gets an e-Marketing Spot by name. |
|
GET |
Deprecated: Gets an e-Marketing Spot at a specific category level. For example, Furniture. Alternatively, this can be implemented using the findByName method with the following URL: store/{storeId}/espot/{name}?categoryId=&DM_ReqCmd=CategoryDisplay. |
|
GET |
Deprecated: Gets an e-Marketing Spot for a specific product. Alternatively, this can be implemented using the findByName method with the following URL: store/{storeId}/espot/{name}?productId=&DM_ReqCmd=ProductDisplay. |
|
GET |
Gets e-Marketing Spot data. |
Conclusion
We have built a functional front-end for HCL commerce using the Gatsby framework. The store has all the basic functionalities needed to perform a checkout, such as add-to-cart and a checkout flow. The user will be able to browser the entire catalog and select different versions of the products to add to their cart.