top of page

Storefront APIs - Wire Adapter

  • Writer: Shane Smyth
    Shane Smyth
  • Aug 12, 2024
  • 4 min read

Configuration can take you a long way with Salesforce Commerce Cloud, all from what products show up in your store to what the branding of the page looks like. There typically is a point though where some level of customization is required, this means getting into code and building custom Lightning Web Components. That's where Storefront APIs come into the picture.


This article is part of a 3 part series where we'll go through each Storefront API and provide examples, the three Storefront APIs are:

  1. Imperative API

  2. Wire Adapter (This article)

  3. Storefront Action


Before we get going though, you might be asking "Why do I need Storefront APIs? I can just use Apex to modify the raw data however i'd like!". Well you're not wrong, however by not using these Storefront APIs you're incurring more technical debt then needed, slowing down your performance, and potentially even opening up your store to access issues.


Using Storefront API provide these benefits (as well as more, but these are the most important):

  1. Salesforce Best Practice

  2. Performance

  3. Lower Effort


Before jumping in, if you'd prefer watching this content, you can view a video on Salesforce Mojo.



Wire Adapters

Wire Adapters specify a GET method to call and retrieve data from a hosted data source. The adapter automatically updates properties in the LWC when hosted source data changes. When the LWC loads or updates, it executes the adapter call and retrieves the data in real time.



We call the wire service reactive in part because it supports reactive variables, which are prefixed with $. If a reactive variable changes, the wire service provisions new data.

As it says in this excerpt, the wire adapter is reactive. Said another way, when updates occur to the data from other sources the latest data will be send to the wire because it's 'listening' for that data proactively.


This example shows the CartItemsAdapter which "Fetches and loads cart items".

@wire(CartItemsAdapter)
	setCartItems({ data, error }) {
        // handling of data & errors
	}
}

This example shows the CheckoutInformationAdapter which "Retrieves data from CheckoutStore, guest email info value from the store, the deliveryGroup, and the first shippingInstructions in the store. Called one time at startup and then whenever the data store updates.".

@wire(CheckoutInformationAdapter, {})
    checkoutInfo({ error, data }) {
        // handling of data & errors
	}
}

CheckoutInformationAdapter Example

Let's use this second wire as an example to discuss the lifecycle, and let's assume we've created a custom LWC in checkout called 'Checkout Sample' which is just responsible for subscribing to this wire and consoling out what it is sent.


The lifecycle will look like the following:

  1. The Checkout Sample Component Loads

    1. CheckoutInformationAdapter returns with checkoutStatus 202

    2. CheckoutInformationAdapter returns with checkoutStatus 202 (This is normally done twice on initial load as the checkout does the initial loading and fetching of data through extensions)

      1. If you'd like see to see the payload difference you can see the sections below

    3. CheckoutInformationAdapter returns with checkoutStatus 200 (stating that checkout is fully loaded and ready to be interacted with

  2. User updates the shipping address (this would be after the initial components load) on checkout

    1. updateShippingAddress imperative API is called, which behind the scenes updates the Checkout Store

  3. Checkout Sample

    1. Receives updates from Checkout Store (In this situation there will be multiple updates sent to your wire as the checkout is processing the changes and moving from 200 -> 202 -> 200 with the new information.)


Checkout Lifecycle
Checkout Lifecycle


If you're interested in a lightweight component to see this same lifecycle, you can find this below.


import { LightningElement, wire } from 'lwc';
import { CheckoutInformationAdapter } from 'commerce/checkoutApi';

export default class CheckoutSample extends LightningElement {
    connectedCallback() {
        this.isPreview = this.isInSitePreview();
    }

    /**
     * 
     * Get the CheckoutData from the standard salesforce adapter
     * Response is expected to be 202 while checkout is starting
     * Response will be 200 when checkout start is complete and we can being processing checkout data 
     * 
     */
    @wire(CheckoutInformationAdapter, {})
    checkoutInfo({ error, data }) {
        if (!this.isPreview) {
            if (data) {

                console.log("##checkoutSample checkoutInfo checkoutInfo: " +JSON.stringify(data));

            } else if (error) {
                console.log("##checkoutSample checkoutInfo Error: " + error);
            }
        } else {
            this.isLoading = false;
        }
    }

    /**
     * Determines if you are in the experience builder currently
     */
    isInSitePreview() {
        let url = document.URL;

        return (
        url.indexOf("sitepreview") > 0 ||
        url.indexOf("livepreview") > 0 ||
        url.indexOf("live-preview") > 0 ||
        url.indexOf("live.") > 0 ||
        url.indexOf(".builder.") > 0
        );
    }
}

Checkout Information - 202

This response is the initial load of the cart which has the high level payload stubbed out.

{
    "cartSummary": {
        "cartId": "0a6DI0000000Ft6",
        "currencyIsoCode": "000",
        "firstPymtGrandTotalAmount": "0",
        "grandTotalAmount": "1205.0",
        "totalChargeAmount": "0",
        "totalProductAmount": "1205.0",
        "totalSubProductCount": "0",
        "totalTaxAmount": "0.0",
        "uniqueProductCount": 2
    },
    "checkoutId": "2z9DI00000002bYYAQ",
    "deliveryGroups": {
        "count": 1,
        "items": [
            {
                "availableDeliveryMethods": [],
                "deliveryAddress": {
                    "city": "Cowlesville",
                    "companyName": "",
                    "country": "US",
                    "firstName": "",
                    "lastName": "",
                    "name": "Shipping",
                    "postalCode": "14037",
                    "region": "NY",
                    "street": "Google Lane"
                },
                "desiredDeliveryDate": "2024-09-08T22:21:04.000Z",
                "id": "0a7DI0000000aCyYAI",
                "isDefault": true,
                "name": "Cart Delivery Group"
            }
        ]
    },
    "errors": [],
    "orderReferenceNumber": "54WIO-HLVJK-5E2Y4-6LXXV",
    "checkoutStatus": 202
}

Checkout Information - 200

In this version you'll notice i've highlighted a few sections, This is specifically the sections that are updated after to moving to 200. If you look carefully, you'll notice these sections are directly connected to the extensions that are running async.

{
    "cartSummary": {
        "cartId": "0a6DI0000000Ft6",
        "currencyIsoCode": "000",
        "firstPymtGrandTotalAmount": "0",
        "grandTotalAmount": "1220.0",
        "totalChargeAmount": "15.0",
        "totalProductAmount": "1205.0",
        "totalSubProductCount": "0",
        "totalTaxAmount": "0.0",
        "uniqueProductCount": 2
    },
    "checkoutId": "2z9DI00000002bYYAQ",
    "deliveryGroups": {
        "count": 1,
        "items": [
            {
                "availableDeliveryMethods": [
                    {
                        "adjustedShippingFee": "15.0",
                        "carrier": "",
                        "classOfService": "",
                        "currencyIsoCode": "USD",
                        "id": "7cdDI00000002E5YAI",
                        "name": "Next Day",
                        "shippingFee": "15.0",
                        "totalAdjustmentAmount": "0.0"
                    },
                    {
                        "adjustedShippingFee": "30.0",
                        "carrier": "",
                        "classOfService": "",
                        "currencyIsoCode": "USD",
                        "id": "7cdDI00000002E6YAI",
                        "name": "Speedy Shipper",
                        "shippingFee": "30.0",
                        "totalAdjustmentAmount": "0.0"
                    }
                ],
                "deliveryAddress": {
                    "city": "Cowlesville",
                    "companyName": "",
                    "country": "US",
                    "firstName": "",
                    "lastName": "",
                    "name": "Shipping",
                    "postalCode": "14037",
                    "region": "NY",
                    "street": "Google Lane"
                },
                "desiredDeliveryDate": "2024-09-08T22:21:04.000Z",
                "id": "0a7DI0000000aCyYAI",
                "isDefault": true,
                "name": "Cart Delivery Group",
                "selectedDeliveryMethod": {
                    "adjustedShippingFee": "15.0",
                    "carrier": "",
                    "classOfService": "",
                    "currencyIsoCode": "USD",
                    "id": "7cdDI00000002E5YAI",
                    "name": "Next Day",
                    "shippingFee": "15.0",
                    "totalAdjustmentAmount": "0.0"
                }
            }
        ]
    },
    "errors": [],
    "orderReferenceNumber": "54WIO-HLVJK-5E2Y4-6LXXV",
    "checkoutStatus": 200
}

Wrap Up

As you can see from the example above, utilizing a Wire can significantly decrease the amount of effort and time it takes to customize. There will be situations where you will need custom data, but in a lot of the standard situations utilizing a wire is the best path forward.


Happy Coding!

 
 

Join the Club

Join our email list and never miss a new article or video launch!

Thanks for submitting!

  • Youtube
  • LinkedIn

©2024 by Salesforce Mojo

bottom of page