Menu
Menu Sheet Overlay
Search
Search Sheet

Commerce Integrations

A customizable API abstraction layer for your ecommerce backend

    Overview

    The Commerce Integrations package is a collection of Connectors for ecommerce backends. A Connector is a customizable API wrapper for a backend and is the data layer that a Mobify application talks to when a user browses, searches or buys products on your site. If you’re interested in the details, see our full API documentation.

    Connectors are Javascript classes that implement the CommerceConnector interface, that standardizes common method names and data types across ecommerce backends.

    We provide pre-built Connectors for Salesforce Commerce Cloud and Hybris APIs, as well as utilities for implementing a custom Connector from scratch. For users on backends without usable APIs we provide a base class and a reference implementation for screen-scraping Connectors. You can use these to build a convenient data layer on top of a traditional HTML backend.

    All Mobify apps are built on top of a Connector. Once set up, you’ll be able to use it as a common data layer for your Progressive Web App (PWA) or server-side app. Connectors integrate well with Mobify’s other SDKs and give you flexibility when modernizing your backend.

    Commerce Integrations Overview

    Connector Basics

    A Connector is an ordinary Javascript class that you can override and extend as you see fit. Connector methods return Promises that resolve to plain Javascript objects. We’ve described those data types in our API docs.

    The main objectives of our Commerce Integrations are (1) to give users without usable APIs a simple data layer on top of which to build applications and (2) to provide flexibility for users interested in migrating between ecommerce backends.

    These concepts will be familiar if you’ve used Mobify’s Integration Manager before – Commerce Integrations are a replacement, with a new interface that makes Connectors easier to extend and re-use outside the context of a Redux application.

    Pre-built Connectors (SFCC and Hybris)

    Our pre-built Connectors are adapters for ecommerce backends. They standardize method names and do conversion to and from Mobify’s ecommerce data types and those of the underlying backend.

    You can extend our built-in Connectors any way you like, provided that your Connector properly implements the CommerceConnector interface. In practice that means that it is safe to transform/add properties to a return value, but not to remove them or change their types.

    All built-in Connectors can be extended/modified in the same way. Taking Salesforce as an example, this is how you could use a Connector in your project:

    
        import {SalesforceConnector} from '@mobify/commerce-integrations/dist/connectors/sfcc'
    
        /**
         * A custom Connector for a project.
         */
        class CustomSalesforceConnector extends SalesforceConnector {
    
            /**
             * Override the default behaviour to return only uppercase product names.
             */
            searchProducts(searchParams, opts = {}) {
                return super.searchProducts(searchParams, opts)
                    .then(data => {
                        data.results = data.results.map((result) => {
                            result.productName = result.productName.toUpperCase()
                            return result
                        })
                        return data
                    })
            }
        }
    
        // Create a connector, with your own config parameters
        const connector = CustomSalesforceConnector.fromConfig({
            basePath: 'https://mobify-tech-prtnr-na03-dw.demandware.net/s/2017refresh/dw/shop/v17_8',
            defaultHeaders: {
                'x-dw-client-id': '5640cc6b-f5e9-466e-9134-9853e9f9db93'
            }
        })
    
        const searchRequest = {filters: {categoryId: 'menswear'}}
        connector.searchProducts(searchRequest)
            .then((result) => {console.log(result)})
    

    Web Scraping Connectors

    Many users start a new project without access to a usable API on their ecommerce backend. In this situation we recommend implementing a Connector by scraping your existing desktop site.

    A web scraping approach requires significant set up because you’ll need to build a Connector from scratch. You’ll also need to take care to avoid global browser dependencies if you want to use your Connector outside of a PWA. Fortunately we have a lot of experience building web scrapers and we’ve packaged our utilities into a base Connector that you can use as a starting point. You just need to follow two rules to keep your Connector usable on the client and the server:

    1. You must inject the window object into the Connector’s constructor.
    2. You must access any browser globals on this.window within the Connector.

    By following these rules we can swap window for a JSDOM instance, essentially giving you an API that you can use to build applications outside of the browser.

    Here’s how that would work, for a fictional store at example.com:

    
        import {ScrapingConnector} from '@mobify/commerce-integrations/dist/connectors/scraping-connector'
    
        /**
         * A web scraping Connector for www.example.com.
         */
        export class CustomWebScrapingConnector extends ScrapingConnector {
    
            constructor({window}) {
                super({window})
                this.basePath = 'https://www.example.com'
            }
    
            /**
             * A searchProducts implementation that uses this.agent and this.buildDocument
             * to fetch a HTML response, build a document and then parse search results
             * out of the page content.
             */
            searchProducts(params) {
                const url = `${this.basePath}/search/${params.filters.category}?count=${params.count}`
                return this.agent.get(url)
                    .then((res) => this.buildDocument(res))
                    .then((htmlDoc) => this.parseSearchProducts(htmlDoc))
            }
    
            /**
             * Typically we write parsers a separate methods that use DOM APIs
             * to parse content out of an HTML response.
             */
            parseSearchProducts(htmlDoc) {
                return {
                    results: htmlDoc.querySelectorAll('.product').map((prod) => ({
                        productName: prod.querySelector('.title').textContent.trim(),
                        price: parseInt(prod.querySelector('.price').textContent.trim()),
                    }))
                }
            }
        }
    

    Now we can use the Connector in a browser:

    
        const connector = new CustomWebScrapingConnector({window: window})
        const searchRequest = {filters: {categoryId: 'menswear'}}
        connector.searchProducts(searchRequest)
            .then((result) => {console.log(result)})
    

    Or on the server:

        import jsdom from 'jsdom'
    
        jsdom.JSDOM.fromURL('https://www.example.com')
            .then((dom) => new CustomWebScrapingConnector({window: dom.window})
            .then((connector) => {
                const searchRequest = {filters: {categoryId: 'menswear'}}
                return connector.searchProducts(searchRequest)
            })
            .then((result) => {console.log(result)})
    

    Types

    We have extensively documented the data types our Commerce Integrations use so that you can understand how to work with our Connectors. We also expose the React Proptypes we use to validate our data types internally, so that you can use these directly in your own React code, eg:

    import PropTypes from 'prop-types'
    import {types} from '@mobify/commerce-integrations/dist/types'
    import React from 'react'
    
    const MyProductList = (props) => {
        const {data} = props
        return (
            <div className="my-product-list">
                {data.results.map((product) =>
                    <div className="name">{product.prudctName}</div>)}
            </div>
        )
    }
    
    MyProductList.propTypes = {
      data: PropTypes.shape(types.ProductSearch)
    }
    

    Using Commerce Connectors With React/Redux

    In a Redux app, such as a Mobify PWA, you will want to use a Connector inside of your Redux actions. Connectors are unaware of your Redux application, but our PWAs use the redux-thunk middleware as standard, which provides a way of injecting dependencies into thunk actions using thunk.withExtraArgument.

    This is a minimal, working React/Redux application using Commerce Integrations:

    /**
     * The simplest working React/Redux application using Commerce Integrations.
     */
    
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider, connect } from 'react-redux'
    import {HybrisConnector} from '@mobify/commerce-integrations/dist/connectors/hybris'
    import {createStore, applyMiddleware, combineReducers} from 'redux'
    import thunk from 'redux-thunk'
    
    // ## Redux reducers
    const products = (state = null, action) => {
      switch (action.type) {
        case 'PRODUCTS_RECEIVED':
          return action.products
        default:
          return state
      }
    }
    
    const reducer = combineReducers({products})
    
    // ## Redux actions
    
    /**
     * This is a simple action.
     */
    const productsReceived = (products) => {
        return {
          type: 'PRODUCTS_RECEIVED',
          products,
        }
    }
    
    /**
     * This is a redux-thunk action.
     * Note that we inject a Connector as the third argument to the thunk.
     */
    const searchProducts = (searchParams) => (dispatch, getState, {connector}) => {
        return connector.searchProducts(searchParams)
            .then((products) => dispatch(productsReceived(products)))
    }
    
    // ## React components
    
    let App = ({products}) => (
        <div>
            <h1>The simplest Product List Demo</h1>
            <div className="product-list">
                {products && products.results.map(product => (
                    <div key={product.productId} className="product">
                        <h2>{product.productName}</h2>
                        <p>${product.price}</p>
                    </div>
                ))}
            </div>
        </div>
    )
    
    App = connect((state) => ({
        products: state.products
    }))(App)
    
    // ## App initialization
    
    const main = () => {
    
        // Create an instance of a Connector. Here we are using the Hybris
        // Connector, unmodified.
    
        const connector = HybrisConnector.fromConfig({
            clientConfig: {
                basePath: `https://hybris.merlinspotions.com/rest/v2/apparel-uk/`,
            },
            catalogId: 'apparelProductCatalog',
            catalogVersionId: 'Online',
            authentication: {
                authorizationUrl: `https://hybris.merlinspotions.com/authorizationserver/oauth/token`,
                clientId: 'mobile_android',
                clientSecret: 'secret'
            }
        })
    
        // Initialize the Redux store as you usually would. However, to
        // make the connector available in redux-thunk actions you must
        // inject it, using thunk.withExtraArgument.
    
        const store = createStore(
          reducer,
          applyMiddleware(thunk.withExtraArgument({connector: connector}))
        )
    
        // Mount your React application
    
        const body = document.getElementsByTagName('body')[0]
        const reactTarget = document.createElement('div')
        reactTarget.id = 'react-target'
        body.appendChild(reactTarget)
        ReactDOM.render(
            <Provider store={store}>
                <App />
            </Provider>,
            reactTarget
        )
    
        // Dispatch an action to search for products
    
        const searchRequest = {filters: {categoryId: '220000'}}
        store.dispatch(searchProducts(searchRequest))
    
    }
    
    main()
    

    This approach makes it easy to swap the Connector used in your actions for a different one. If you’re writing tests for your actions, you also have the option to inject a mock Connector to prevent your tests from making real API requests.

    Summary

    We’ve seen how to use Commerce Integrations to build a data layer on top of your existing backend. We hope that users with Salesforce or Hybris APIs will find our Connectors convenient starting points for new applications. For users without API-enabled backends, we hope the patterns we’ve shown here will help you unlock the data on your site and use it in new and exciting ways.