Vue Storefront is now Alokai! Learn More
Integrating Algolia Search

Integrating Algolia Search

Replace the default product search in your Alokai Storefront project with Algolia

CommerceIs Algolia integration supported?
BigCommerce
Commercetools
Adobe Commerce (Magento)
SAPCC
SFCC

Algolia is an extremely fast search solution popular among eCommerce stores. This guide covers integrating Algolia Search for the Product Listing and Search pages.

To integrate Algolia Search, you must extend the Unified Data Layer to replace the default endpoint for product search (SearchProducts) with a dedicated one based on the Algolia API. This extension introduces two separate normalizers responsible for transforming data from the format returned by Algolia into Unified Data Layer format and vice-versa.

If you want to use other Algolia features, such as autocomplete, infinite search, or search suggestions, you need to implement them yourself. The integration we provide is limited to the Product Listing Page and Search Page.

Algolia Setup

The starting point is to feed Algolia with data from your eCommerce instance. For some platforms, Algolia provides dedicated plugins that will fetch data and create indexes based on it. If this is the case for you, you can use this solution. However, if Algolia does not support your eCommerce platform, you need to create a .json file yourself, which you will use to feed the indexes in Algolia. Learn more about this in the Algolia documentation.

Once the data from your eCommerce is in Algolia, create additional indexes that will contain sorted data. First, decide how many and what sorting options you want to provide to your storefront users, then create a Replica Index for each of these options. Algolia itself recommends this approach to sorting - see more in the documentation.

The data structure in Algolia largely depends on the data import option you chose. If you used a ready-made data import plugin, the data structure will be pre-determined based on the existing solution. However, if you imported the .json file yourself, you have full control over it.

Regardless of how you importat data, you'll need to create a dedicated normalizer in the Unified Data Layer that will transform the Algolia data structure into the UDL data structure.

An example .json structure of the Algolia record may looks as follows:

[
  {
    "productID": "30404",
    "sku": "30404",
    "slug": {
      "en": "30404-shades-von-zipper-papa-g-black-gloss-black-gloss-grey",
      "de": "30404"
    },
    "name": {
      "en": "Shades Von Zipper Papa G black gloss black gloss/grey"
    },
    "categories": [
      {
        "name": "Clothes",
        "id": "clothes"
      },
      {
        "name": "Sunglasses",
        "id": "sunglasses"
      }
    ],
    "attributes": {
      "size": {},
      "color": {},
      "ean": "299052695"
    },
    "images": "/medias/?context=bWFzdGVyfGltYWdlc3wxMzUyNXxpbWFnZS9qcGVnfGFHSXlMMmcxWXk4NE56azJOalF3TnpNNU16VTR8ODkzYTA5NjRlMGQ2ZjZjNDE5OTBlZGRhYTUwZjM5YTU5NGNjZDVlNGI2NDI4MTdlMzRkMWMwOTI0MTdiNzlhMQ",
    "prices": {
      "EUR": 7788,
      "USD": 10533
    },
    "gender": ["FEMALE"],
    "stores": ["Cambridge University", "Edinburgh University"]
  }
]

When looking at your Algolia records, it's important to note that:

  • the data structure in Algolia is not the same as the data structure in the Unified Data Layer. To work with these records in your storefront, you need to create a dedicated normalizer that will transform the Algolia data structure into the UDL data structure
  • the Algolia record contains some fields, as for example stores, gender, and attributes field, which are not present in the UDL. These fields can be used to construct the facets in the Algolia configuration.
  • the categories field contains an array of objects with the name and id fields. The id field should used to configure facets. In the Algolia response, only the categories ids will be returned, and to populate the category names, you can use the categoryFacet field in the Alokai Algolia addon configuration.

Alokai Algolia Integration

Let's start by examining a typical UDL installation for the SAPCC in the middleware.config.ts file.

import {
  Config,
  Context,
  methods,
  normalizers,
  SfFacetTypes,
  createUnifiedExtension,
} from "@vsf-enterprise/unified-api-sapcc";
import { ApiClientExtension, WithoutContext } from "@vue-storefront/middleware";
import { getSapccConfig } from "./config";
import { createPaymentAndPlaceOrder } from "@sf-modules-middleware/checkout";

// eslint-disable-next-line @typescript-eslint/no-var-requires
require("dotenv").config();

const { CNTF_TOKEN, CNTF_SPACE, CNTF_ENVIRONMENT, SAPCC_MEDIA_HOST } = process.env;

const apiMethods = methods<typeof normalizers>();
export const unifiedApiExtension = createUnifiedExtension<Context, Config>()({
  normalizers,
  apiMethods: {
    ...apiMethods,
    createPaymentAndPlaceOrder,
  },
  config: {
    transformImageUrl: (url) => {
      if (SAPCC_MEDIA_HOST) {
        return new URL(url, SAPCC_MEDIA_HOST).toString();
      }

      const [imagePathWithoutParams, searchParams = ""] = url.split("?");
      const segmentsParameter = imagePathWithoutParams.split("/").filter(Boolean);
      const sapContextSearchParameter = new URLSearchParams(searchParams).get("context");

      return `sap/${segmentsParameter}/context/${sapContextSearchParameter}`;
    },
    currencies: ["USD", "EUR"],
    defaultCurrency: "USD",
    getFacetType: (facet) => {
      if (facet.name === "Colour") {
        return "COLOR";
      }
      if (facet.name === "Size") {
        return "SIZE";
      }
      if (facet.name === "Category") {
        return "CATEGORY";
      }

      return facet.multiSelect ? SfFacetTypes.MULTI_SELECT : SfFacetTypes.SINGLE_SELECT;
    },
    filterFacets: (facet) => !(facet.category && facet.name !== "Category"),
  },
});

const config = {
  integrations: {
    cntf: {
      location: "@vsf-enterprise/contentful-api/server",
      configuration: {
        token: CNTF_TOKEN,
        space: CNTF_SPACE,
        environment: CNTF_ENVIRONMENT,
      },
    },
    commerce: {
      ...getSapccConfig(),
      extensions: (extensions: ApiClientExtension[]) => [...extensions, unifiedApiExtension],
    },
  },
};

export default config;

Now we will modify the operation of our unifiedApiExtension by adding an Algolia addon to it. For SAP Commerce Cloud and Salesforce Commerce Cloud, the Algolia addon is available in the Unified API Integration package and can be imported from @vsf-enterprise/unified-api-[sapcc/sfcc]/algolia.

Since the Algolia addon is included in the base package, you don't need to install any additional packages.

The steps to integrate Algolia into Alokai are as follows:

  1. Add imports for Algolia methods.
  2. Modify normalizers to include those required by the Algolia addon.
  3. Add dedicated settings for the Algolia addon.
  4. Integrate Algolia into the Middleware configuration.

The rest of this guide is built for SAP Commerce Cloud

However, if you're using Salesforce Commerce Cloud, the steps are largely the same.

Add Imports

The first step is to add the relevant imports from the Algolia addon. Since both the Algolia addon and the SAP Commerce Cloud APIs export methods and normalizers, we recommend renaming them to avoid conflicts.

middleware.config.ts
import {
  Context,
  Config,
  methods as methodsSAPAlgolia,
  normalizers as normalizersSAPAlgolia,
  EnhancedNormalizers,
} from "@vsf-enterprise/unified-api-sapcc/algolia";
// Update the current import methods for SAP
import {
  methods as methodsSAP,
  normalizers as normalizersSAP,
  defineNormalizers,
} from "@vsf-enterprise/unified-api-sapcc";

Register Algolia normalizers and methods

Next, add the Algolia normalizers and methods to the Middleware configuration. The ordering of the normalizers is important, as the Algolia normalizers should override the SAP normalizers.

This is also the place where you should define your own normalizers to transform the structure of your Algolia records into the Unified Data Model structure.

middleware.config.ts
const normalizers = defineNormalizers<EnhancedNormalizers>()({
  ...normalizersSAP,
  ...normalizersSAPAlgolia,
  // Here you should define code for extra normalizers required by Algolia
  normalizeAlgoliaProductCatalogItem(product: any, ctx: NormalizerContext<string>): SfProductCatalogItem {
    // add your own normalization
  };
})

const apiMethods = {
  ...methodsSAP<typeof normalizers>(),
  ...methodsSAPAlgolia<typeof normalizers>(),
}

The @vsf-enterprise/unified-api-sapcc/algolia package exports the normalizers, but in most cases you still will have to define your own normalizers, at least for the normalizeAlgoliaProductCatalogItem, for the Algolia addon. The reason for this is that the Algolia data structure is not the same as the UDL data structure. Therefore, you need to create a dedicated normalizer that will transform your Algolia product record into the UDL data structure.

Configure Algolia Addon

Next, you can set up the Algolia configuration in your Unified Extension.

The available configuration options are:

  • categoryFacet - string
    • The name of the category facet. The facet should contain ids of categories. It is used to populate the category names during the normalization, so if the value is provided, the normalized category SfFacet will include the category id and name.
  • facets - string[]
    • The facets that will be returned by the Algolia search. The facets should be defined in the Algolia dashboard.
  • getFacetLabel - (facet: AlgoliaFacet) => string
    • The function receives the facet and should return the label for the facet.
  • indexName - string | (args: SearchProductArgs) => string
    • String name of the Algolia index or a function that returns the name of the Algolia index based on the arguments.
  • integrationName - string
    • The name of the integration registered in middleware.config.ts file that will be used to get the Algolia client.
export const unifiedApiExtension = createUnifiedExtension<Context, Config>()({
  normalizers,
  apiMethods: {
    ...apiMethods
    createPaymentAndPlaceOrder,
  },
  config: {
    transformImageUrl: (url) => {
      if (SAPCC_MEDIA_HOST) {
        return new URL(url, SAPCC_MEDIA_HOST).toString();
      }

      const [imagePathWithoutParams, searchParams = ""] = url.split("?");
      const segmentsParameter = imagePathWithoutParams.split("/").filter(Boolean);
      const sapContextSearchParameter = new URLSearchParams(searchParams).get("context");

      return `sap/${segmentsParameter}/context/${sapContextSearchParameter}`;
    },
    currencies: ["USD", "EUR"],
    defaultCurrency: "USD",
    getFacetType(facet) {
      switch (facet) {
        case "attributes.color.en": {
          return "COLOR";
        }
        case "attributes.size.en": {
          return "SIZE";
        }
        case "categories.id": {
          return "CATEGORY";
        }
        default: {
          return SfFacetTypes.MULTI_SELECT;
        }
      }
    },
    algolia: {
      categoryFacet: "categories.id",
      integrationName: "algolia",
      facets: ["*"],
      getFacetLabel(facet) {
        return (
          {
            "attributes.size.en": "Size",
            "attributes.color.en": "Color",
            stores: "Stores",
          }[facet] ?? facet
        );
      },
      indexName({ sortBy }) {
        switch (sortBy) {
          case "price-low-to-high": {
            return "products_sapcc_price_asc";
          }
          case "price-high-to-low": {
            return "products_sapcc_price_desc";
          }
          default: {
            return "products_sapcc";
          }
        }
      },
    },
  },
});

Please note that the algolia property is a dedicated configuration for the Algolia addon. It contains the following properties:

  • categoryFacet - the name of the facet that will be used to filter products by category.
  • integrationName - the name of the integration that will be used to identify the Algolia integration.
  • facets - an array of facets that will be used to filter products.
  • getFacetLabel - a function that returns the label for a given facet.
  • indexName - a function that returns the name of the index that will be used to filter products.

To specify the facets, you can use the attributes.* syntax, which will return all attributes from the Algolia record. You can also use the * syntax, which will return all fields from the Algolia record. Please remember also that the facets you specify here must be configured in the Algolia index. Example of the Algolia attributes for faceting configuration:

Example configuration for Algolia facets

Add Algolia to the Middleware Configuration

Finally, you need to add the Algolia integration to the Middleware configuration.

const { CNTF_TOKEN, CNTF_SPACE, CNTF_ENVIRONMENT, SAPCC_MEDIA_HOST, ALGOLIA_API_KEY, ALGOLIA_APP_ID } = process.env;

// ...

const config = {
  integrations: {
    algolia: {
      location: "@vsf-enterprise/algolia-api/server",
      configuration: {
        apiKey: ALGOLIA_API_KEY,
        appId: ALGOLIA_APP_ID,
        // Set your Index name here
        defaultIndex: "default",
        // Set your facets
        defaultFacets: [],
      },
    },
    commerce: {
      // ...
    },
  },
};

And that's it. Now you should be able to use Algolia in your storefront on Product Listing Page and Search Page.