Headless commerce usage

This is for:

Developer

The Coveo Headless library acts as a middle-layer for applications, opening a line of communication between the UI elements and the Commerce API.

A project built on top of Headless will typically involve two main building-blocks:

  • the engine, which manages the state of the product discovery interface and communicates with the Coveo Platform

  • the controllers, which dispatch actions to the engine based on user interactions

Install Headless

Use npm to install the Headless library.

npm install @coveo/headless

Initialize the commerce engine

Tip

All controllers must depend on a single CommerceEngine instance. This instance is responsible for managing the state across all commerce solutions (such as search, recommendations, listings).

For more details on the initialization options given below, see buildCommerceEngine.

import { buildCommerceEngine } from '@coveo/headless/commerce';
import { loadCartItemsFromLocalStorage } from '../utils/cart-utils';

export const getEngine = () => {
  if (_engine !== null) {
    return _engine;
  }

  _engine = buildCommerceEngine({
    configuration: {
      organizationId: '<ORGANIZATION_ID>', 1
      accessToken: '<ACCESS_TOKEN>', 2
      analytics: { 3
        trackingId: '<TRACKING_ID>'
      },
      context: { 4
        currency: '<CURRENCY>',
        country: '<COUNTRY>',
        language: '<LANGUAGE>',
        view: {
          url: '<URL>'
        },
      }
      cart: {
        items: loadCartItemsFromLocalStorage() ?? [], 5
      },
    },
  });

  return _engine;
};
1 Organization ID is the unique identifier of your Coveo organization.
2 Access token is a search token or an API key that grants the Allowed access level on the Execute Queries domain and the Push access level on the Analytics Data domain in the target organization.

To improve security, client-side specification of user IDs isn’t supported by Event Protocol. To specify user IDs, enforce them through search tokens.

3 Via the analytics object, specify the tracking ID.
4 The context object contains information about the user’s context, such as the currency, country, language, and the URL.

Every time the user navigates between pages, you must update this URL.

5 Pass in the initial state of the cart by specifying the CartInitialState object.

In this code sample, a custom loadCartItemsFromLocalStorage function is used to initialize the cart state by fetching the cart items from local storage. To learn more about how to correctly manage the cart state, see Managing the Cart State.

For more details on how an engine is initialized in an actual project, see the sample in the Headless repository

Headless controllers

A Headless controller is an abstraction that simplifies the implementation of a specific Coveo-powered UI feature or component. In other words, controllers provide programming interfaces for interacting with the Headless engine’s state.

Headless provides a set of controllers for each commerce solution (such as search, recommendations, and listings). Additionally, there are controllers for common features such as interacting with the cart or modifying context.

Initialize a controller instance

You can initialize a Headless controller by calling its builder function. A controller’s builder function always requires a Headless engine instance as a first argument.

import { buildSearch } from "@coveo/headless/commerce";
const search = buildSearch(commerceEngine);

Many builder functions also accept, and sometimes require, a configuration object as a second argument.

In the following example, you can initialize a Recommendations controller with an initial state that specifies your recommendations configuration information.

import { buildRecommendations } from "@coveo/headless/commerce";
const recommendations = buildRecommendations(commerceEngine, {
  options: {
    slotId: "<SLOT_ID>",
    productId: "<PRODUCT_ID>"
  },
});

Interact with a controller

The Commerce Headless engine exposes different controllers. Each controller exposes a public interface, which you can use to interact with the Headless engine’s state.

You can either call a method directly on a controller, or create a sub-controller first, and then call a method on the sub-controller.

For example, certain common functionality such as sorting, pagination, and facets are available as sub-controllers on each commerce solution controller you’re using (such as Search, Recommendations, and ProductListing).

When you call a method on a controller instance (or a sub-controller instance), one or more actions are dispatched. The Headless engine’s reducers listen to those actions and react to them by altering the state as necessary.

The following code sample showcases how to interact with a search controller by directly calling methods and how to use it to create sub-controllers.

import { buildSearch } from "@coveo/headless/commerce";
const search = buildSearch(commerceEngine); 1

search.executeFirstSearch(); 2

const facetGenerator = search.facetGenerator(); 3
const facets = facetGenerator.facets; 4
1 Use the buildSearch builder function to create a search controller instance.
2 Call the executeFirstSearch method on the search controller instance to execute the first search.
3 Use the facetManager method on the search controller instance to create a FacetGenerator sub-controller instance that will manage facets.
4 Call the facets getter on the sub-controller to retrieve a list of facets.

Note that due to the descriptive nature of the Commerce API, which Headless is using under the hood, the list of facets returned by Coveo aren’t specified in the front-end.

Subscribe to state changes

You can use the subscribe method on a controller instance to listen to its state changes. The listener function you pass when calling the subscribe method will be executed every time an action is dispatched that affects the state that’s relevant to the controller.

The following code sample showcases how you can run a custom function every time the state of the search controller changes.

import { buildSearch } from "@coveo/headless/commerce";

const search = buildSearch(commerceEngine);

const onSearchStateUpdate = () => {
  // custom code
}

search.subscribe(onSearchStateUpdate);

Dispatch actions

"You’ll often dispatch methods in response to changes in the state. However, you may want to dispatch actions manually, when you need more fine-grained control over a feature than what a controller offers through its public interface.

You can create actions using action loaders and dispatch them using the Headless engine’s dispatch method.

import { loadQueryActions } from "@coveo/headless/commerce";

const queryActions = loadQueryActions(commerceEngine); 1
const action = queryActions.updateQuery({query: '<QUERY>'}); 2
commerceEngine.dispatch(action); 3
1 Use the loadQueryActions function to add the necessary reducers to the engine, if they haven’t been added already, and return an object holding the relevant action creator functions.
2 Create a dispatchable action by calling the updateQuery action creator function, passing in the updated query.
3 Dispatch the action by calling the dispatch method on the commerce engine instance.

For more details on the available actions, see the reference documentation.